使用索引

时间:2016-04-29 01:03:03

标签: mysql database group-by database-indexes

我在AWS m4.large(2个vCPU,8 GB ram)上运行,我看到了关于MySQL和GROUPBY的一个略微令人惊讶的行为。我有这个测试数据库:

CREATE TABLE demo (
  time INT,
  word VARCHAR(30),
  count INT
);
CREATE INDEX timeword_idx ON demo(time, word);

我使用(统一)随机字"t%s" % random.randint(0, 30000)和时间random.randint(0, 86400)插入4,000,000条记录。

SELECT word, time, sum(count) FROM demo GROUP BY time, word;
3996922 rows in set (1 min 28.29 sec)

EXPLAIN SELECT word, time, sum(count) FROM demo GROUP BY time, word;
+----+-------------+-------+-------+---------------+--------------+---------+------+---------+-------+
| id | select_type | table | type  | possible_keys | key          | key_len | ref  | rows    | Extra |
+----+-------------+-------+-------+---------------+--------------+---------+------+---------+-------+
|  1 | SIMPLE      | demo  | index | NULL          | timeword_idx | 38      | NULL | 4002267 |       |
+----+-------------+-------+-------+---------------+--------------+---------+------+---------+-------+

然后我不使用索引:

SELECT word, time, sum(count) FROM demo IGNORE INDEX (timeword_idx) GROUP BY time, word;
3996922 rows in set (34.75 sec)

EXPLAIN SELECT word, time, sum(count) FROM demo IGNORE INDEX (timeword_idx) GROUP BY time, word;
+----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra                           |
+----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+
|  1 | SIMPLE      | demo  | ALL  | NULL          | NULL | NULL    | NULL | 4002267 | Using temporary; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+

正如您所看到的那样,通过使用索引,查询需要花费3倍的时间。我并不感到惊讶,因为通过使用索引,查询可能必须避免阅读timeword列,但不幸的是,索引如此稀疏,它不应该获得太多。相反,它在检索count时将直接扫描转换为随机访问模式。

我只是想确认这就是原因,并想知道是否有一个"紧凑的规则" on when和index在用于GROUP BY时最终会带来更差的性能。

编辑:

我跟着Gordon Linoff的回答并使用了:

CREATE INDEX timeword_idx ON demo(time, word, count);

"覆盖索引"与完整扫描相比,计算结果的速度提高了10倍:

SELECT word, time, sum(count) FROM demo GROUP BY time, word;
3996922 rows in set (3.36 sec)

EXPLAIN SELECT word, time, sum(count) FROM demo GROUP BY time, word;
+----+-------------+-------+-------+---------------+--------------+---------+------+---------+-------------+
| id | select_type | table | type  | possible_keys | key          | key_len | ref  | rows    | Extra       |
+----+-------------+-------+-------+---------------+--------------+---------+------+---------+-------------+
|  1 | SIMPLE      | demo  | index | NULL          | timeword_idx | 43      | NULL | 4002267 | Using index |
+----+-------------+-------+-------+---------------+--------------+---------+------+---------+-------------+

非常令人印象深刻!

2 个答案:

答案 0 :(得分:3)

您有一个合理大小的表,因此问题可能是数据的顺序访问或颠簸。使用索引需要遍历索引,然后查找数据页中的数据以获取count

这实际上可能比仅仅阅读页面和进行排序更糟糕,因为页面不是按顺序读取的。顺序读取比随机读取更加优化。在最坏的情况下,页面缓存已满,随机读取需要刷新页面。如果发生这种情况,可能需要多次读取单个页面。只有400万个相对较小的行,除非你受到严重的内存限制,否则不太可能发生颠簸。

如果这种解释是正确的,那么在索引中加入count可以加快查询速度:

CREATE INDEX timeword_idx ON demo(time, word, count);

答案 1 :(得分:1)

从手册页How MySQL Uses Indexes

  

索引对于小型表或大型表的查询不太重要   报表查询处理大多数或所有行的位置。当一个查询   需要访问大多数行,顺序读取比快   通过索引工作。顺序读取可以最大限度地减少磁盘搜索   如果不是查询所需的所有行。

至于在更多列上创建覆盖索引(未访问数据页但索引中的所有数据都可用),请注意。它们需要付出代价。在您的情况下,您的索引无论如何都要变宽。但总是需要谨慎平衡。

正如斯宾塞所暗示的那样,基数总是在范围内发挥作用。有关基数信息,请使用web命令。它不是您的查询的驱动问题,但在其他设置中很有用。我应该重申一下:你的桌子的基数非常高。因此,您的索引在该查询中被视为阻碍它。