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 个操作可以通过索引加速:

  1. 过滤数据范围
  2. 确保每个 link_id 只返回一次。

如果您真的想优化这个查询,那么您可以在 link_id, date_added 个字段上创建一个多列索引。您在对戈登回答的评论中写道,这是不可能的。

因此,MySQL 必须决定使用其中一个索引来加速 2 个操作中的哪一个。它决定 link_id 索引是更好的选择。如果您不同意它的决定,那么您可以使用 index hints 向 MySQL 表明您更愿意使用(use indexforce index)或忽略(ignore index ) 一个特定的索引。

只需指示MySQL忽略link_id索引并使用date_added索引。请确保在调整后检查查询速度。