了解 EXPLAIN 语句的输出 (MySQL)
Posted
技术标签:
【中文标题】了解 EXPLAIN 语句的输出 (MySQL)【英文标题】:Understanding the output of an EXPLAIN statement (MySQL) 【发布时间】:2012-10-31 10:05:22 【问题描述】:我有一个查询返回以下说明:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE this_ index_merge CategoryId,Deleted,PublishedOn,_ComputedDeletedValue,_Temporary_Flag,Published,_TestItemSessionGuid Deleted,_ComputedDeletedValue,_Temporary_Flag,Published 1,1,1,1 6203 Using intersect(Deleted,_ComputedDeletedValue,_Temporary_Flag,Published); Using where
这是否表明查询到处都在使用索引,还是可以通过添加其他索引来改进?基于此:http://dev.mysql.com/doc/refman/5.1/en/explain-output.html,它提到key
列显示了实际使用的索引。我对每一列都有一个索引。
有问题的查询如下:
SELECT SQL_NO_CACHE count(*) FROM
article this_
WHERE
(this_._Temporary_Flag = 0 OR this_._Temporary_Flag = NULL) AND
this_.Published = 1 AND
(this_.PublishedOn IS NULL OR this_.PublishedOn <= '2012-10-30 18:46:18 ') AND
(this_.Deleted = 0 OR this_.Deleted = NULL) AND
(this_._ComputedDeletedValue = 0 OR this_._ComputedDeletedValue = NULL) AND
((this_._TestItemSessionGuid IS NULL OR this_._TestItemSessionGuid = ''))
AND NOT (this_.CategoryId IS NULL)
表有大约 140,000 条记录。此查询需要 3 秒的时间执行,并返回 135,725 作为结果。
【问题讨论】:
在 where 子句中:“Temporary_Flag = NULL)” @tombom 感谢您指出这一点。我已经删除了'Is NULL'检查,因为它没用,因为该字段是布尔值,甚至除了NULL
值!
【参考方案1】:
解释表明 MySQL 正在使用由 4 个独立索引合并而成的索引,键长度为 1,1,1,1,这意味着所有四列都用于遍历搜索树。
但是,在所有列上设置单独的索引通常不是最有效的方法。特别是在您的情况下,合并四个索引可能需要很多时间。实际执行可能会更快,但构建索引可能需要 1-2 秒。
我建议在这些列上建立一个复合索引。这些事情的顺序。获取具有相同条件的那些并将它们按基数顺序排列(更大的基数首先)。最后一列将是范围查询(在您的情况下为 PublishedOn)。
例如:
create index my_query_IDX on article (Deleted, _Temporary_Flag, _ComputedDeletedValue, PublishedOn)
我建议的另一件事是将 _Temporary_Flag、Deleted、_ComputedDeletedValue、_Published 等列更改为 NOT NULL DEFAULT '0'。可空列和空值上的索引不如非空列有效,根据 key_length,我看到这些列是 BOOLEANS 或 TINYINT(顺便说一句,它们是相同的)。
【讨论】:
这可能很愚蠢,但你所说的“基数顺序”到底是什么意思?您会建议更改原始查询,还是仅添加索引?AND NOT (this_.CategoryId IS NULL)
之前的块就像查询的“标准”部分,因为这些字段在所有表中都很常见,我不能轻易修改查询的那部分,最好。
@KarlCassar 在布尔列上有索引我不推荐。请看我对另一个问题的回答。 ***.com/questions/12296258/… 除此之外,我同意 Károly Nagy 的观点。 +1
通过“基数顺序”,可能值的变化越大,基数越大。例如,布尔值的基数为 2(真或假),这不是很有效。在大多数情况下,即使您有索引,MySQL 也会进行全扫描搜索。在您的情况下,它可能会起作用。请注意,拥有良好的索引高度依赖于数据本身,而不仅仅是架构。我喜欢电影数据库的例子:如果你有一个where title like "Scarf%"
但不会用于where title like "The%"
,那么在电影标题上有一个索引非常好。
我创建了 Károly Nagy 指定的索引,它极大地改进了搜索,对于大多数查询将其缩短到 0.08 秒。但是,我仍然无法理解为什么选择该选项,例如为什么没有添加“已发布”但“已删除”。
答案是数据库中的数据分布。如果 80% 的文章都发表了,那么 mysql 使用索引并不能带来太多好处。在这种情况下,全扫描比遍历 b+tree 更便宜。优化索引不仅关乎模式,还关乎数据。以上是关于了解 EXPLAIN 语句的输出 (MySQL)的主要内容,如果未能解决你的问题,请参考以下文章