为什么MySQL没有为这个选择查询使用索引?

时间:2015-07-23 16:30:54

标签: mysql innodb database-indexes

为什么此查询不在此处使用索引?该表使用InnoDB引擎。

explain SELECT null as id,
    ->  up_time,
    ->  reg_date,
    ->  refer,
    ->  MAX(IFNULL(visits_count,0)) as visits_count,
    ->  MAX(IFNULL(register_count,0)) as register_count,
    ->  MAX(IFNULL(players_count,0)) as players_count,
    ->  MAX(IFNULL(activity_count,0)) as activity_count,
    ->  MAX(IFNULL(payment_users_count,0)) as payment_users_count,
    ->  MAX(IFNULL(payment_count,0)) as payment_count,
    ->  MAX(IFNULL(payment_sum,0)) as payment_sum FROM stats_refers
    ->
    ->  WHERE
    ->
    ->  stats_refers.reg_date < 1435006800
    ->  AND stats_refers.up_time < 1435006800
    ->
    ->  GROUP BY stats_refers.refer, stats_refers.reg_date;

解释:

+----+-------------+--------------+------+----------------------------+------+---------+------+---------+----------------------------------------------+
| id | select_type | table        | type | possible_keys              | key  | key_len | ref  | rows    | Extra                                        |
+----+-------------+--------------+------+----------------------------+------+---------+------+---------+----------------------------------------------+
|  1 | SIMPLE      | stats_refers | ALL  | reg_date,stat,up_reg_index | NULL | NULL    | NULL | 2983126 | Using where; Using temporary; Using filesort |
+----+-------------+--------------+------+----------------------------+------+---------+------+---------+----------------------------------------------+

可以使用的密钥:

+--------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table        | Non_unique | Key_name     | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| stats_refers |          0 | PRIMARY      |            1 | id          | A         |     2983126 |     NULL | NULL   |      | BTREE      |         |               |
| stats_refers |          0 | reg_date     |            1 | reg_date    | A         |       13317 |     NULL | NULL   |      | BTREE      |         |               |
| stats_refers |          0 | reg_date     |            2 | up_time     | A         |     1491563 |     NULL | NULL   |      | BTREE      |         |               |
| stats_refers |          0 | reg_date     |            3 | refer       | A         |     2983126 |     NULL | NULL   |      | BTREE      |         |               |
| stats_refers |          1 | stat         |            1 | reg_date    | A         |       15142 |     NULL | NULL   |      | BTREE      |         |               |
| stats_refers |          1 | stat         |            2 | refer       | A         |       28683 |     NULL | NULL   |      | BTREE      |         |               |
| stats_refers |          1 | refer_uptime |            1 | refer       | A         |        2307 |     NULL | NULL   |      | BTREE      |         |               |
| stats_refers |          1 | refer_uptime |            2 | up_time     | A         |     1491563 |     NULL | NULL   |      | BTREE      |         |               |
| stats_refers |          1 | up_reg_index |            1 | reg_date    | A         |        2314 |     NULL | NULL   |      | BTREE      |         |               |
| stats_refers |          1 | up_reg_index |            2 | up_time     | A         |     1491563 |     NULL | NULL   |      | BTREE      |         |               |
+--------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

以下是表格说明:

CREATE TABLE `stats_refers` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `reg_date` int(10) unsigned NOT NULL,
  `up_time` int(10) unsigned NOT NULL,
  `refer` varchar(16) NOT NULL DEFAULT '',
  `visits_count` int(10) unsigned NOT NULL DEFAULT '0',
  `register_count` int(10) unsigned NOT NULL DEFAULT '0',
  `players_count` int(10) unsigned NOT NULL DEFAULT '0',
  `activity_count` int(10) unsigned NOT NULL DEFAULT '0',
  `payment_users_count` int(10) unsigned NOT NULL DEFAULT '0',
  `payment_count` int(10) unsigned NOT NULL DEFAULT '0',
  `payment_sum` int(10) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `reg_date` (`reg_date`,`up_time`,`refer`),
  KEY `stat` (`reg_date`,`refer`),
  KEY `refer_uptime` (`refer`,`up_time`),
  KEY `up_reg_index` (`reg_date`,`up_time`)
) ENGINE=InnoDB AUTO_INCREMENT=4136504 DEFAULT CHARSET=utf8 |
+----+-------------+--------------+-------+-----------------------------------+-------------+---------+------+---------+-------------+
| id | select_type | table        | type  | possible_keys                     | key         | key_len | ref  | rows    | Extra       |
+----+-------------+--------------+-------+-----------------------------------+-------------+---------+------+---------+-------------+
|  1 | SIMPLE      | stats_refers | index | reg_date,up_time,reg,search_index | group_index | 54      | NULL | 3011896 | Using where |
+----+-------------+--------------+-------+-----------------------------------+-------------+---------+------+---------+-------------+

2 个答案:

答案 0 :(得分:2)

我假设创建用于此分组操作的复合索引stat与执行您尝试执行的分组操作的顺序相反。要使索引可用于此分组操作,您需要执行以下操作之一:

  • 撤消stat索引
  • 上的列顺序
  • refer
  • 上添加单个索引
  • referreg_date
  • 上添加另一个复合索引

您做出的决定最终需要考虑表中的其他查询操作。

您可能希望更广泛地考虑您的索引使用情况。虽然使用复合索引可以提高性能,但是对于可能需要在表的查询范围内使用的每个字段使用单独的索引,在您的情况下,您将在表描述中的不同组合中复制相同字段的索引。如果不了解所有查询用例,您可能会很难查看整体索引建议,但我只想指出您可能想要考虑这一点。

例如,up_reg_index根本没有明显需要,因为reg_date唯一索引已经涵盖了索引。您可能最好使用一组这样的索引:

PRIMARY KEY (`id`),
UNIQUE KEY `regdate_uptime_refer` (`reg_date`,`up_time`,`refer`),
KEY `reg_date` (`reg_date`),
KEY `refer` (`refer`),
KEY `up_time` (`up_time`),

这肯定比您目前拥有的索引空间要少,并且可以更灵活地在这些列上过滤/加入/分组,但不要将此作为一个坚定的建议。根据不同的查询方案测试不同索引方案的性能(特别是如果是用例,则插入性能)。

答案 1 :(得分:0)

INDEX(refer, reg_date)
中的
GROUP BY

可能对引用INDEX(up_time) 的{​​{1}}部分有用。

WHERE

优化程序将使用up_time reg_date < 1435006800 AND up_time < 1435006800 考虑,但前提是“范围”小于20%的表。它不太可能尝试使用两个单独的索引并进行“索引合并”。

由于INDEX(reg_date, ...)中的“范围”,因此无法拥有同时处理INDEX(up_time, ...)WHERE的索引。

没有其他索引对该查询有用。

这20%是不精确的。它的存在是因为如果需要查看过多的表,那么执行表扫描实际上比使用索引要快。

您正在选择WHERE,但不会按其分组。那很顽皮。该查询可以提供它感觉到的任何ORDER BY

为什么有一个up_time,因为您的up_time密钥可能是id?我问,因为PK的“聚类”可能导致这是最好的选择:

UNIQUE

(当然,改变这可能会弄乱其他查询。)