【问题标题】:analysing what to improve via an EXPLAIN query on MySQL通过对 MySQL 的 EXPLAIN 查询分析需要改进的地方
【发布时间】:2013-11-08 08:56:21
【问题描述】:

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

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

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 查询的重要输出是什么?

【问题讨论】:

    标签: mysql indexing database-performance


    【解决方案1】:

    这是说它选择使用的索引是名为 _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;
    

    【讨论】:

    • 我真的需要学会更快地输入我的答案;特别是当我单击“显示 1 个新答案”按钮并看到一个与我将要提交的几乎相同的查询时。
    【解决方案2】:

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

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

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

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

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

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

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

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

    因此,假设 emailusername 将比此键更具选择性,您可以尝试将查询重写为 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 和奇怪的关键选择作为您可能可以改进的指标。

    【讨论】:

    • 不幸的是,我使用的是 ORM,并且更改查询并不容易 - 添加更适合该查询的索引会更容易。这可能吗?
    • 索引在将AND 条件组合在一起时效果最佳,并且具有极高的选择性:意思是,只有一条或几条记录与AND-ed together 条件匹配。根据您的 WHERE 子句,我认为新索引不会有帮助(尽管它取决于您的数据)。如果您的手被 ORM 束缚,您可以尝试执行两个单独的查询(一个用于上面的每个 UNIONed 查询),然后将它们合并到应用程序层中。您使用的是哪个 ORM?
    • 正如@WillemRenzema 所说,索引与AND 配合得很好。使用和 OR(如在这种情况下)它们非常无用。如果可以选择在电子邮件或用户名上使用索引,那么无论它选择使用哪个索引,它仍然不得不扫描每一行数据以检查不在它选择的索引中的另一个值。您可以添加的唯一可能有一点帮助的索引是 _Deleted 和 _Temporary_Flag 列上的覆盖索引,但我怀疑它的基数会很低,我怀疑它会有多大帮助。
    • 所以如果我必须删除ORs,我认为可以这样做,它会加快速度吗?我会试一试,然后告诉你。
    • @WillemRenzema 它正在使用 Nhibernate
    猜你喜欢
    • 2014-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 2021-08-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多