【问题标题】:MySQL, reading this EXPLAIN statementMySQL,阅读此 EXPLAIN 语句
【发布时间】:2010-09-18 08:23:55
【问题描述】:

我有一个查询开始在我的应用程序中引起一些关注。我试图更好地理解这个 EXPLAIN 语句,以了解可能缺少索引的位置:

+----+-------------+--------+--------+------------- --+------------+---------+------------ --------+------+----------------------------------+ |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+--------+--------+------------- --+------------+---------+------------ --------+------+----------------------------------+ | 1 |简单 |小号 |参考 |客户 ID |客户 ID | 4 |常量 | 102 |使用临时的;使用文件排序 | | 1 |简单 |你| eq_ref |初级 |初级 | 4 | www_foo_com.s.user_id | 1 | | | 1 |简单 |一个 |参考 | session_id | session_id | 4 | www_foo_com.s.session_id | 1 |使用索引 | | 1 |简单 | h |参考 | email_id | email_id | 4 | www_foo_com.a.email_id | 10 |使用索引 | | 1 |简单 |酸碱度 |参考 | session_id | session_id | 4 | www_foo_com.s.session_id | 1 |使用索引 | | 1 |简单 |时间 |参考 | session_id | session_id | 4 | www_foo_com.s.session_id | 1 | | | 1 |简单 |河粉 |参考 | session_id | session_id | 4 | www_foo_com.s.session_id | 1 | | | 1 |简单 | c |全部 |用户字段 |空 |空 |空 | 1108 | | +----+-------------+--------+--------+------------- --+------------+---------+------------ --------+------+---------------------------------+ 8 行一组(0.00 秒)

我试图通过阅读这个 EXPLAIN 语句来了解我的索引在哪里丢失。不看查询,只看EXPLAIN的结果就可以理解如何优化这个查询,这样说公平吗?

似乎对“c”表的 ALL 扫描是致命弱点。根据 MySQL 文档中推荐的常量值来索引它的最佳方法是什么? |

请注意,我还在 cdr 表中的用户字段中添加了一个索引,但效果也不太好。

谢谢。

--- 编辑---

这是查询,抱歉——不知道为什么我在第一次通过时忽略了它。

SELECT s.`session_id` id,
                  DATE_FORMAT(s.`created`,'%m/%d/%Y') date,
                  u.`name`,
                  COUNT(DISTINCT c.id) calls,
                  COUNT(DISTINCT h.id) emails,
                  SEC_TO_TIME(MAX(DISTINCT c.duration)) duration,
                  (COUNT(DISTINCT em.email_id) + COUNT(DISTINCT pho.phone_id) > 0) status
           FROM `fa_sessions` s
           LEFT JOIN `fa_users` u ON s.`user_id`=u.`user_id`
           LEFT JOIN `fa_email_aliases` a ON a.session_id = s.session_id
           LEFT JOIN `fa_email_headers` h ON h.email_id = a.email_id
           LEFT JOIN `fa_phones` ph ON ph.session_id = s.session_id
           LEFT JOIN `fa_email_aliases` em ON em.session_id = s.session_id AND em.status = 1
           LEFT JOIN `fa_phones` pho ON pho.session_id = s.session_id AND pho.status = 1
           LEFT JOIN `cdr` c ON c.userfield = ph.phone_id
           WHERE s.`partner_id`=1
           GROUP BY s.`session_id`      

【问题讨论】:

  • 尝试使用 FORCE INDEX e 看看该计划会发生什么。示例:LEFT JOIN cdr c FORCE INDEX (index_name_for_userfield) ON c.userfield = ph.phone_id。用它来回答确实可以解决它。

标签: sql mysql database database-design


【解决方案1】:

我假设您已经查看here 以获取有关它告诉您的更多信息。显然 ALL 意味着它要经过所有这些。在该页面上讨论了使用临时和使用文件排序。你可能想看看那个。

从页面:

使用文件排序

MySQL 必须做一个额外的 pass 才能找到 找出如何检索已排序的行 命令。排序是通过 go 根据连接遍历所有行 键入并存储排序键和 指向所有行的行的指针 匹配 WHERE 子句。钥匙然后 已排序并检索行 按排序顺序。见第 7.2.12 节, “按优化排序”。

使用临时

要解析查询,MySQL 需要 创建一个临时表来保存 结果。这通常发生在 查询包含 GROUP BY 和 ORDER BY 以不同方式列出列的子句。

我同意查看查询可能有助于更好地解决问题。

【讨论】:

    【解决方案2】:

    我的建议?

    将查询一分为二,中间使用临时表。

    推理

    问题似乎是表 c 正在被表扫描,并且这是查询中的最后一个表。这可能很糟糕:如果您有一个表扫描,您想在查询开始时执行它,所以它只执行一次。

    我不是 MySQL 专家,但我花了很多时间优化其他数据库上的查询。在我看来,优化器还没有解决它应该以 c 开头并向后工作。

    让我印象深刻的另一件事是连接中的表可能太多了。大多数优化器都在处理超过 4 个表(因为可能的表顺序的数量呈指数级增长,因此检查它们变得不切实际)。
    我所见过的 90% 的性能问题的根源是连接中的表过多。

    试一试,让我们知道您的进展情况。如果还是不行,请贴出SQL、表定义和索引,我再看看。

    一般提示

    随时查看this answer 我提供的一般性能提示。

    很好的资源

    MySQL Documentation for EXPLAIN

    【讨论】:

      【解决方案3】:

      仔细查看查询会很有用,但至少有一件事情显然值得研究 - 最后一行显示查询的那部分的 ALL 类型,这通常不太好看到。如果建议的可能键(用户字段)作为表 c 的添加索引有意义,则可能值得添加它并查看这是否会减少搜索中为该表返回的行。

      【讨论】:

        【解决方案4】:

        查询计划

        我们可能希望优化器选择的查询计划类似于:

        • sessions 开头,其中partner_id=1,可能使用partner_id, 上的索引
        • 使用user_id 上的索引将sessions 加入users
        • sessions 加入phones,其中status=1,使用session_id 上的索引,可能还有status
        • 使用session_idphone_id 上的索引再次将sessions 加入phones **
        • 使用userfield 上的索引将phones 加入cdr
        • sessions 加入email_aliases,其中status=1 使用session_id 上的索引,可能还有status
        • 使用session_idemail_id 上的索引再次将sessions 加入email_aliases **
        • 使用email_id 上的索引将email_aliases 加入email_headers

        ** 通过在这些索引中放置 2 个字段,我们使优化器能够使用 session_id 加入表,并立即找出关联的 phone_idemail_id,而无需读取基础表。这种技术为我们节省了阅读时间,并且可以节省大量时间。

        我将创建的索引:

        上述查询计划建议以下指标:

        fa_sessions ( partner_id, session_id )  
        fa_users ( user_id )  
        fa_email_aliases ( session_id, email_id )  
        fa_email_headers ( email_id )  
        fa_email_aliases ( session_id, status )  
        fa_phones ( session_id, status, phone_id ) 
        cdr ( userfield ) 
        

        注意事项

        • 您几乎肯定会获得可接受的性能,而无需创建所有这些。
        • 如果任何表都很小(少于 100 行),则可能不值得创建索引。
        • fa_email_aliases 可能与 ( session_id, status, email_id ) 一起使用,具体取决于优化器的工作方式。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-12-03
          • 1970-01-01
          • 1970-01-01
          • 2013-01-01
          • 2015-10-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多