我的MySQL慢查询日志显示的查询看起来很简单,就像我服务器上运行最慢的查询之一:
SELECT result_known,AVG(points_total) as points
FROM tbl_points
WHERE uid IN (N,{1023 repeats}N)
GROUP BY gid
ORDER BY gid ASC;
我基本上试图找到一个组的子组(一组uid,例如基于性别或其他)的平均点总数。 uid和gid上有单独的索引,但EXPLAIN
表明它们没有被使用:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
| 1 | SIMPLE | tbl_points | ALL | combined | NULL | NULL | NULL | 64 | Using where; Using temporary; Using filesort
现在,根据我的理解,显而易见的解决方案是在这些字段上设置覆盖索引:
CREATE INDEX index1 ON dbo.tbl_points(result_known, points_total, uid, gid)
事实上,这使得它使用索引:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
| 1 | SIMPLE | tbl_points | index | combined | index3 | 18 | NULL | 64 | Using where; Using index; Using temporary; Using filesort |
但是,我有两个问题:
在Extra
字段中,EXPLAIN
现在说" 使用where;使用索引;使用临时;使用filesort "。这很糟糕,对吧?那么我应该使用这个索引吗?在虚拟术语中,type=index
和key=something
比在"额外"中发生的更重要。领域,还是没有?
在大型刀片上设置覆盖指数有什么影响?我通过使用一个非常大的临时表执行JOIN
- UPDATE
来在同一个表中插入点。我不想太慢地放慢速度。
答案 0 :(得分:1)
Using Temporary
表示创建了一个临时表以满足条件组。这不是很糟糕,但如果optimise group by用于“松散索引扫描”,则可以获得更好的性能。
为了使此查询避免使用临时表,必须对gid
列编制索引,使其成为复合键或单列索引的最左侧部分。为了进一步改进它,uid
作为单个列索引也是一个很好的选择:
CREATE INDEX uid_idx ON dbo.tbl_points(uid)
CREATE INDEX gid_idx ON dbo.tbl_points(gid)
<强>更新强>
正如@Dow正确指出的那样,AVG()
的使用取消了通过计算查询索引访问组的资格,只有MIN()
和MAX()
不会。尽管如此,建议的指数仍应提供更好的表现。
答案 1 :(得分:1)
通常,您可以使用索引来优化IN(...)
的范围谓词,或者您可以使用索引来优化由GROUP BY
引起的临时表(尽管您提出了一个很好的观点)这可能不适用于AVG())。但是,您无法在同一个SELECT中实现索引的两种使用。
我将得出结论,您无法摆脱此特定查询中的临时表。您可以做的最好的事情就是通过增加tmp_table_size
来防止它进入磁盘。或者如果它确实转到磁盘,请配置tmpfs文件系统并将该挂载点用作tmpdir
。
因此您必须选择,是否要在索引中搜索uid
值列表?你有一个非常长的uid列表,所以估计行数会花费很多。一定要升级到MySQL 5.6,它在这方面有一些新的优化(见Equality Range Optimization of Many-Valued Comparisons)。
type=index
意味着它正在进行索引扫描,这是昂贵的,但至少它只能从索引中获取结果而不必读取表行。因此,它需要更少的缓冲池页面来满足此查询。