覆盖MySQL中的索引决策

时间:2014-06-12 15:49:37

标签: php mysql sql

我的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=indexkey=something比在"额外"中发生的更重要。领域,还是没有?

  • 在大型刀片上设置覆盖指数有什么影响?我通过使用一个非常大的临时表执行JOIN - UPDATE来在同一个表中插入点。我不想太慢地放慢速度。

2 个答案:

答案 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意味着它正在进行索引扫描,这是昂贵的,但至少它只能从索引中获取结果而不必读取表行。因此,它需要更少的缓冲池页面来满足此查询。