0

我不太精通 MySQL 中的索引,并且很难理解EXPLAIN输出是如何工作的,以及如何读取它以了解我的查询是否经过优化。

我有一个相当大的表(1.1M 记录),我正在执行以下查询:

SELECT * FROM `Member` this_ WHERE (this_._Temporary_Flag = 0 or this_._Temporary_Flag 
is null) and (this_._Deleted = 0 or this_._Deleted is null) and 
(this_.Username = 'XXXXXXXX' or this_.Email = 'XXXXXXXX') 
ORDER BY this_.Priority asc;

执行需要很长时间,大部分时间在 30 到 60 秒之间。查询的输出EXPLAIN如下:

id  select_type  table  type         possible_keys                            key              key_len  ref    rows   Extra                        
----------------------------------------------------------------------------------------------------------------------------------
1   SIMPLE       this_  ref_or_null  _Temporary_Flag,_Deleted,username,email  _Temporary_Flag  2        const  33735  Using where; Using filesort  

这句话究竟是什么意思?这是否意味着可以优化此查询?该表主要具有单列索引。EXPLAIN我应该使用的查询的重要输出是什么?

4

2 回答 2

0

http://dev.mysql.com/doc/refman/5.5/en/explain-output.html

解释告诉你 MySQL 正在做什么,它不一定告诉你甚至暗示可以做些什么来让事情变得更好。

也就是说,有一些警告信号通常表明您可以优化查询;在这种情况下,最大的一个是出现Using filesort在 Extra 列中。

该文档解释了在这种情况下会发生什么:

MySQL 必须做一个额外的过程来找出如何按排序顺序检索行。排序是通过根据连接类型遍历所有行并存储排序键和指向与 WHERE 子句匹配的所有行的行的指针来完成的。然后对键进行排序,并按排序顺序检索行。

在您的情况下,另一个警告标志key是使用的。虽然在您的情况下不一定正确,但良好规范化的结构通常需要Username和的唯一值Email

那么,为什么在指定这两件事时要花这么长时间呢?优化器不应该能够直接进入那些行吗?可能不会,因为您使用 指定它们OR,这使得优化器很难使用索引来查找这些行。

相反,优化器决定_Temporary_Flag查看所有结果,这可能并没有缩小结果集的范围,特别是考虑到解释说大约查看了 33735 行。

因此,假设email并且username将比此键更具选择性,您可以尝试将查询重写为 UNION。

SELECT * FROM `Member` this_ 
WHERE 
(this_._Temporary_Flag = 0 or this_._Temporary_Flag 
is null) 
and 
(this_._Deleted = 0 or this_._Deleted is null)
and this_.Email = 'XXXXXXXX'
UNION 
SELECT * FROM `Member` this_ 
WHERE (this_._Temporary_Flag = 0 or this_._Temporary_Flag 
is null) 
and (this_._Deleted = 0 or this_._Deleted is null) 
and 
this_.Username = 'XXXXXXXX'
ORDER BY this_.Priority asc;

所以,这些是来自 EXPLAIN 的几个警告信号:寻找Using filesort和奇怪的关键选择作为你可能改进事情的指标。

于 2013-11-08T09:48:34.580 回答
0

据说它选择使用的索引是名为 _Temporary_Flag 的索引(我假设它位于 _Temporary_Flag 列上)。这不是一个很好的索引(它仍然会查看 33k 条记录),但它可以在这种情况下使用最好。可能值得添加一个涵盖 _Temporary_Flag 和 _Deleted 列的索引。

但是我怀疑这会缩小范围。

一个问题是 MySQL 只能在查询中对表使用单个索引。可能要使用的最佳索引在用户名上,另一个在电子邮件上,但由于您的查询有一个 OR,它必须选择一个或另一个。

绕过对索引的这种限制的一种方法是使用联合在一起的 2 个查询,如下所示:-

SELECT * 
FROM `Member` this_ 
WHERE (this_._Temporary_Flag = 0 
or this_._Temporary_Flag is null) 
and (this_._Deleted = 0 
or this_._Deleted is null) 
and this_.Email = 'XXXXXXXX'
UNION
SELECT * 
FROM `Member` this_ 
WHERE (this_._Temporary_Flag = 0 
or this_._Temporary_Flag is null) 
and (this_._Deleted = 0 
or this_._Deleted is null) 
and this_.Username = 'XXXXXXXX' 
ORDER BY this_.Priority asc;
于 2013-11-08T09:45:15.667 回答