MySQL select 不使用索引的不同查询
MySQL select distinct query not using index
我有一个 table clicks
:
CREATE TABLE `clicks` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`link_id` int(11) NOT NULL,
`date_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
PRIMARY KEY (`id`),
KEY `link_id` (`link_id`),
KEY `date_added` (`date_added`)
) ENGINE=InnoDB AUTO_INCREMENT=90899051 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
具有以下指标:
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| clicks | 0 | PRIMARY | 1 | id | A | 79808649 | NULL | NULL | | BTREE | | |
| clicks | 1 | link_id | 1 | link_id | A | 276154 | NULL | NULL | | BTREE | | |
| clicks | 1 | date_added | 1 | date_added | A | 79808649 | NULL | NULL | | BTREE | | |
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
我正在尝试 运行 对此 table 进行一些分析查询,但我发现 运行 需要很长时间。以下面的查询为例:
SELECT
DISTINCT(link_id) AS link_id
FROM
clicks
WHERE
date_added >= '2016-11-01 00:00:00'
AND date_added <= '2016-12-05 10:16:00'
这个查询花了将近一分钟的时间才完成。我通过 运行ning EXPLAIN
查询发现未使用索引。
+----+-------------+--------+-------+---------------+---------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+---------+---------+------+----------+-------------+
| 1 | SIMPLE | clicks | index | date_added | link_id | 4 | NULL | 79786609 | Using where |
+----+-------------+--------+-------+---------------+---------+---------+------+----------+-------------+
我预计查询会是 运行,方法是使用 date_added
列上的索引过滤结果集,然后从结果。
有谁知道为什么索引没有被使用,或者我是否可以做些什么来强制使用它?
注意:这个问题是一个更大问题的一部分,与我上周发布的一个未解决的问题密切相关 - MySQL query with JOIN not using INDEX
编辑
解释我的查询,不使用任何索引提示:
EXPLAIN SELECT DISTINCT(link_id) FROM clicks WHERE date_added >= '2016-11-01 00:00:00' AND date_added <= '2016-12-05 23:59:59';
+----+-------------+---------------------------+-------+---------------+---------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------------+-------+---------------+---------+---------+------+----------+-------------+
| 1 | SIMPLE | clicks | index | date_added | link_id | 4 | NULL | 79816660 | Using where |
+----+-------------+---------------------------+-------+---------------+---------+---------+------+----------+-------------+
使用索引提示解释我的查询:
EXPLAIN SELECT DISTINCT(link_id) FROM clicks USE INDEX(date_added) IGNORE INDEX(link_id) WHERE date_added >= '2016-11-01 00:00:00' AND date_added <= '2016-12-05 23:59:59';
+----+-------------+---------------------------+------+---------------+------+---------+------+----------+------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------------+------+---------------+------+---------+------+----------+------------------------------+
| 1 | SIMPLE | clicks | ALL | date_added | NULL | NULL | NULL | 79816882 | Using where; Using temporary |
+----+-------------+---------------------------+------+---------------+------+---------+------+----------+------------------------------+
编辑 2
在我的查询中使用 FORCE INDEX(date_added)
(查询完成得更快,12.05 秒):
EXPLAIN SELECT DISTINCT(link_id) FROM clicks FORCE INDEX(date_added) WHERE date_added >= '2016-11-01 00:00:00' AND date_added <= '2016-12-05 23:59:59';
+----+-------------+---------------------------+-------+---------------+------------+---------+------+----------+------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------------+-------+---------------+------------+---------+------+----------+------------------------------+
| 1 | SIMPLE | clicks | range | date_added | date_added | 4 | NULL | 17277508 | Using where; Using temporary |
+----+-------------+---------------------------+-------+---------------+------------+---------+------+----------+------------------------------+
如果您有单个链接的 table,您可以尝试:
select l.link_id
from links l
where exists (select 1
from clicks c
where c.link_id = l.link_id and
c.date_added >= '2016-11-01 00:00:00' and
c.date_added <= '2016-12-05 10:16:00'
);
为此,您需要 clicks(link_id, date_added)
上的索引。
首先,没有使用索引是不正确的。 explain
结果清楚地表明使用了 link_id
索引。
至于为什么不使用date_added
索引,答案很简单:MySQL决定不使用,因为它认为link_id
索引会是更好的选择。
您需要了解的是,您的查询中有 2 个操作可以通过索引加速:
- 过滤数据范围
- 确保每个
link_id
只返回一次。
如果您真的想优化这个查询,那么您可以在 link_id, date_added
个字段上创建一个多列索引。您在对戈登回答的评论中写道,这是不可能的。
因此,MySQL 必须决定使用其中一个索引来加速 2 个操作中的哪一个。它决定 link_id
索引是更好的选择。如果您不同意它的决定,那么您可以使用 index hints 向 MySQL 表明您更愿意使用(use index
或 force index
)或忽略(ignore index
) 一个特定的索引。
只需指示MySQL忽略link_id
索引并使用date_added
索引。请确保在调整后检查查询速度。
我有一个 table clicks
:
CREATE TABLE `clicks` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`link_id` int(11) NOT NULL,
`date_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
PRIMARY KEY (`id`),
KEY `link_id` (`link_id`),
KEY `date_added` (`date_added`)
) ENGINE=InnoDB AUTO_INCREMENT=90899051 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
具有以下指标:
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| clicks | 0 | PRIMARY | 1 | id | A | 79808649 | NULL | NULL | | BTREE | | |
| clicks | 1 | link_id | 1 | link_id | A | 276154 | NULL | NULL | | BTREE | | |
| clicks | 1 | date_added | 1 | date_added | A | 79808649 | NULL | NULL | | BTREE | | |
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
我正在尝试 运行 对此 table 进行一些分析查询,但我发现 运行 需要很长时间。以下面的查询为例:
SELECT
DISTINCT(link_id) AS link_id
FROM
clicks
WHERE
date_added >= '2016-11-01 00:00:00'
AND date_added <= '2016-12-05 10:16:00'
这个查询花了将近一分钟的时间才完成。我通过 运行ning EXPLAIN
查询发现未使用索引。
+----+-------------+--------+-------+---------------+---------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+---------+---------+------+----------+-------------+
| 1 | SIMPLE | clicks | index | date_added | link_id | 4 | NULL | 79786609 | Using where |
+----+-------------+--------+-------+---------------+---------+---------+------+----------+-------------+
我预计查询会是 运行,方法是使用 date_added
列上的索引过滤结果集,然后从结果。
有谁知道为什么索引没有被使用,或者我是否可以做些什么来强制使用它?
注意:这个问题是一个更大问题的一部分,与我上周发布的一个未解决的问题密切相关 - MySQL query with JOIN not using INDEX
编辑
解释我的查询,不使用任何索引提示:
EXPLAIN SELECT DISTINCT(link_id) FROM clicks WHERE date_added >= '2016-11-01 00:00:00' AND date_added <= '2016-12-05 23:59:59';
+----+-------------+---------------------------+-------+---------------+---------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------------+-------+---------------+---------+---------+------+----------+-------------+
| 1 | SIMPLE | clicks | index | date_added | link_id | 4 | NULL | 79816660 | Using where |
+----+-------------+---------------------------+-------+---------------+---------+---------+------+----------+-------------+
使用索引提示解释我的查询:
EXPLAIN SELECT DISTINCT(link_id) FROM clicks USE INDEX(date_added) IGNORE INDEX(link_id) WHERE date_added >= '2016-11-01 00:00:00' AND date_added <= '2016-12-05 23:59:59';
+----+-------------+---------------------------+------+---------------+------+---------+------+----------+------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------------+------+---------------+------+---------+------+----------+------------------------------+
| 1 | SIMPLE | clicks | ALL | date_added | NULL | NULL | NULL | 79816882 | Using where; Using temporary |
+----+-------------+---------------------------+------+---------------+------+---------+------+----------+------------------------------+
编辑 2
在我的查询中使用 FORCE INDEX(date_added)
(查询完成得更快,12.05 秒):
EXPLAIN SELECT DISTINCT(link_id) FROM clicks FORCE INDEX(date_added) WHERE date_added >= '2016-11-01 00:00:00' AND date_added <= '2016-12-05 23:59:59';
+----+-------------+---------------------------+-------+---------------+------------+---------+------+----------+------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------------+-------+---------------+------------+---------+------+----------+------------------------------+
| 1 | SIMPLE | clicks | range | date_added | date_added | 4 | NULL | 17277508 | Using where; Using temporary |
+----+-------------+---------------------------+-------+---------------+------------+---------+------+----------+------------------------------+
如果您有单个链接的 table,您可以尝试:
select l.link_id
from links l
where exists (select 1
from clicks c
where c.link_id = l.link_id and
c.date_added >= '2016-11-01 00:00:00' and
c.date_added <= '2016-12-05 10:16:00'
);
为此,您需要 clicks(link_id, date_added)
上的索引。
首先,没有使用索引是不正确的。 explain
结果清楚地表明使用了 link_id
索引。
至于为什么不使用date_added
索引,答案很简单:MySQL决定不使用,因为它认为link_id
索引会是更好的选择。
您需要了解的是,您的查询中有 2 个操作可以通过索引加速:
- 过滤数据范围
- 确保每个
link_id
只返回一次。
如果您真的想优化这个查询,那么您可以在 link_id, date_added
个字段上创建一个多列索引。您在对戈登回答的评论中写道,这是不可能的。
因此,MySQL 必须决定使用其中一个索引来加速 2 个操作中的哪一个。它决定 link_id
索引是更好的选择。如果您不同意它的决定,那么您可以使用 index hints 向 MySQL 表明您更愿意使用(use index
或 force index
)或忽略(ignore index
) 一个特定的索引。
只需指示MySQL忽略link_id
索引并使用date_added
索引。请确保在调整后检查查询速度。