为什么 MySQL 查询需要 1 毫秒到 7 秒的时间?

Ant*_*doi 3 mysql sql

我有一个 SQL 查询(见下文),它准确地返回我需要的内容,但是当运行 phpMyAdmin 时,它需要从 0.0009 秒到 0.1149 秒,有时甚至高达 7.4983 秒。

询问:

SELECT
  e.id,
  e.title,
  e.special_flag,
  CASE WHEN a.date >= '2013-03-29' THEN a.date ELSE '9999-99-99' END as date
  CASE WHEN a.date >= '2013-03-29' THEN a.time ELSE '99-99-99' END as time,
  cat.lastname,
  FROM e_table as e
  LEFT JOIN a_table as a ON (a.e_id=e.id)
  LEFT JOIN c_table as c ON (e.c_id=c.id)
  LEFT JOIN cat_table as cat ON (cat.id=e.cat_id)
  LEFT JOIN m_table as m ON (cat.name=m.name AND cat.lastname=m.lastname)
  JOIN (
          SELECT DISTINCT innere.id
          FROM e_table as innere
          LEFT JOIN a_table as innera ON (innera.e_id=innere.id AND
                                          innera.date >= '2013-03-29')
          LEFT JOIN c_table as innerc ON (innere.c_id=innerc.id)
          WHERE (
                  (
                    innera.date >= '2013-03-29' AND 
                    innera.flag_two=1
                  ) OR 
                  innere.special_flag=1
                ) AND
                innere.flag_three=1 AND 
                innere.flag_four=1
          ORDER BY COALESCE(innera.date, '9999-99-99') ASC,
                   innera.time ASC,
                   innere.id DESC LIMIT 0, 10
       ) AS elist ON (e.id=elist.id)
  WHERE (a.flag_two=1 OR e.special_flag) AND e.flag_three=1 AND e.flag_four=1
  ORDER BY a.date ASC, a.time ASC, e.id DESC
Run Code Online (Sandbox Code Playgroud)

解释计划: 上面的查询解释计划

问题是:这个查询的哪一部分可能导致性能的广泛差异?

Nie*_*jes 5

要专门回答您的问题:它不是导致广泛性能的查询的特定部分。这就是 MySQL 做它应该做的事情——成为一个关系数据库管理系统 (RDBMS),而不仅仅是一个围绕逗号分隔文件的愚蠢的 SQL 包装器。

执行查询时,会发生以下情况:

  1. 该查询被编译为“参数化”查询,消除了纯结构 SQL 的所有变量。
  2. 检查编译缓存以查找是否为查询找到了最近可用的执行计划。
  3. 如果需要,查询会被编译成执行计划(这是“EXPLAIN”显示的内容)
  4. 对于每个执行计划元素,都会检查内存缓存是否包含新的和可用的数据,否则从主表数据组装中间数据。
  5. 通过将所有中间数据放在一起来组装最终结果。

您所看到的是,当查询花费 0.0009 秒时,缓存足够新鲜以将所有数据一起提供,当它在 7.5 秒达到峰值时,要么查询表中的某些内容发生了更改,要么其他查询“推送”了内存缓存数据,或者 DBMS 有其他原因怀疑它需要重新编译查询或再次获取所有数据。可能其他一些变化与使用的索引是否仍然在内存中足够新鲜地缓存有关。

总结一下,查询速度慢得离谱,有时您很幸运,缓存使它看起来很快。

为了解决这个问题,我建议研究两件事:

  1. 首先也是最重要的 - 这种大小的查询在其执行计划中不应有一行读取“没有可能的键”。研究索引如何工作,确保您意识到 MySQL 对每个连接表使用单个索引的限制的影响,并调整您的数据库,以便计划的每一行在“键”下都有一个条目。
  2. 其次,检查查询本身。当 DBMS 所要做的就是组合原始数据时,它们的速度是最快的。使用像CASE和这样的编程元素COALESCE通常很有用,但它们确实迫使数据库在运行时评估更多的东西,而不仅仅是获取原始表数据。尝试消除此类语句,或将它们移到业务逻辑中作为对检索数据的后处理。

最后,永远不要忘记 MySQL 实际上是一个相当愚蠢的 DBMS。它针对大多数网站所需的简单数据获取查询的性能进行了优化。因此,对于大多数通用问题,它比 SQL Server 和 Oracle 快得多。一旦你开始用函数、案例、巨大的连接或匹配条件等使事情复杂化,竞争对手通常会得到更好的优化,并且在他们的查询编译器中有更好的优化。因此,当 MySQL 在特定查询中开始变慢时,请考虑将其拆分为 2 个或更多个较小的查询,以免混淆,并使用 PHP 或您正在调用的任何语言进行一些后处理。我已经看到很多情况,这大大提高了性能,只是不混淆 MySQL,尤其是在涉及子查询的情况下(如您的情况)。