我在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倍的时间。我并不感到惊讶,因为通过使用索引,查询可能必须避免阅读time
和word
列,但不幸的是,索引如此稀疏,它不应该获得太多。相反,它在检索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 |
+----+-------------+-------+-------+---------------+--------------+---------+------+---------+-------------+
非常令人印象深刻!
答案 0 :(得分:3)
您有一个合理大小的表,因此问题可能是数据的顺序访问或颠簸。使用索引需要遍历索引,然后查找数据页中的数据以获取count
。
这实际上可能比仅仅阅读页面和进行排序更糟糕,因为页面不是按顺序读取的。顺序读取比随机读取更加优化。在最坏的情况下,页面缓存已满,随机读取需要刷新页面。如果发生这种情况,可能需要多次读取单个页面。只有400万个相对较小的行,除非你受到严重的内存限制,否则不太可能发生颠簸。
如果这种解释是正确的,那么在索引中加入count
可以加快查询速度:
CREATE INDEX timeword_idx ON demo(time, word, count);
答案 1 :(得分:1)
索引对于小型表或大型表的查询不太重要 报表查询处理大多数或所有行的位置。当一个查询 需要访问大多数行,顺序读取比快 通过索引工作。顺序读取可以最大限度地减少磁盘搜索 如果不是查询所需的所有行。
至于在更多列上创建覆盖索引(未访问数据页但索引中的所有数据都可用),请注意。它们需要付出代价。在您的情况下,您的索引无论如何都要变宽。但总是需要谨慎平衡。
正如斯宾塞所暗示的那样,基数总是在范围内发挥作用。有关基数信息,请使用web
命令。它不是您的查询的驱动问题,但在其他设置中很有用。我应该重申一下:你的桌子的基数非常高。因此,您的索引在该查询中被视为阻碍它。