【问题标题】:How to determine cause of slow queries如何确定查询慢的原因
【发布时间】:2016-01-26 14:12:26
【问题描述】:

我有一个大约需要 7 秒的查询。我将它分成两个查询,提供与第一个相同的数据,大约需要 0.01 秒。我原以为我正确地索引了它,但是,可能没有。数据库目前只有很少的数据。我正在使用 MySQL 5.5.46。我正在使用带有 PDO 的 PHP,但是,我认为这无关紧要,并且故意没有用 PHP 或 PDO 标记这个问题。

我不是在问为什么我的特定查询需要这么长时间或如何识别慢速查询,而是要求通用步骤来确定慢速查询的原因。我希望EXPLAIN 会被使用。您在EXPLAIN 中寻找什么?还可以采取哪些其他步骤?

【问题讨论】:

标签: mysql


【解决方案1】:

Spencer7593 是一个很好的起点,但您不会在那里或 StackOverflow 上获得完整的答案。部分解释带我去about 40 pages full

EXPLAIN 很有用-但需要在了解表和索引的结构的情况下阅读-从您的描述来看,优化器似乎忽略了索引。您可以强制数据库使用use a particular index 进行查询,但它是一个相当不整洁的解决方案(即使您知道这是当今最好的解决方案,但将来可能不会)。

如果您有一个非常好的索引并且 DBMS 没有使用它,那么最可能的原因是 cardinality stats have not been updated - 但是当数据非常倾斜时也会发生这种情况(例如,如果您有 10000 个 ' A' 和 'B' 中的 2 个,那么索引将帮助您找到具有 'B' 的记录,但不能找到具有 'A' 的记录)。

始终使用索引并不总是让您的查询更快 - 从单个文件顺序读取比随机读取 2 个文件快得多。

另一个警告是 MySQL 不能很好地处理推送谓词。

注意连接中的隐式(和显式)类型转换 - MySQL 不能对这些使用索引。 Mariadb 支持虚拟列(可以被索引)。因此,如果你

...
tab_a INNER JOIN tab_b
ON UNIX_TIMESTAMP(tab_a.datetime)=tab_b.seconds_since_epoch

优化器可以使用 tab_b.seconds_since_epoch 上的索引,但不能使用 tab_a.datetime 上的索引。

对于某些引擎(以及使用命名锁),查询可能会被 DBMS 中的其他活动阻塞 - 尽管这种情况通常体现在基于统计数据的 DBMS 性能分析中,并且不太可能是这里的原因。还需要另一个步骤来追踪阻塞的原因。

将查询分解为更小的部分并独立测试它们是一种出色的诊断工具(赞!),但只有当您查看所有 EXPLAIN 计划时,您才能理解为什么您会在组合中出现异常行为。

【讨论】:

    【解决方案2】:

    最重要的是,如果您有可能使用 phpmyadmin,那么有一个很棒的分析器。

    在 phpmyadmin 中调用查询后,您可以选择使用“分析”(在编辑锚点之前)

    它为您提供了一个包含工作和时间安排的漂亮图表,所以我认为它会有所帮助。

    【讨论】:

    • 不确定这是否会有所帮助,但愿意尝试一下。我在 phpmyadmin 上寻找它,但找不到它。我正在使用 phpmyadmin 4.0.10.11。
    【解决方案3】:

    Explain 显示子查询、实际使用的索引、必须扫描的行数等。 见mysql manual on its output

    然后有一个神奇的“盯着它”的方法,通常会产生关于如何降低查询复杂性的想法:

    • 查询越少越好
    • 索引优于全表扫描
    • 连接比子查询更好
    • 连接越少越好(因为连接会增加扫描的行数,有时会增加多次)
    • 选择性更强的索引比选择性更低的索引要好,因此索引后需要扫描的行数更少
    • 分组和排序需要额外费用
    • having 可能比 where 更贵(因为分组后有效)

    等等

    【讨论】:

    • 啊,神奇的“盯着它”的方法!一直盯着看,但它不起作用:(rows 列的含义是什么?
    • @user1032531 显示 mysql 在此查询中必须扫描多少行才能产生结果。理想的查询有0(它甚至存在-select 42 as meaning_of_life
    【解决方案4】:

    这很笼统,但我会尝试提供一些指导

    • 第一个是Index,如果您对某个字段执行搜索,则需要该字段的索引。
    • 现在,如果您对多个字段而不是多个索引执行索引,您可能需要一个复合索引。
      • 过滤一个不使用索引的子查询,所以如果你试图过滤一个 子查询。
      • 同样使用WHERE 上的函数不使用索引,例如SUBSTRINGUPPER CASELIKE
    • 在不使用ON 的情况下使用INNER JOIN 将导致CROSS JOIN 并快速增加行数。

    Query Execution Plan 中,您尝试查找FULL SEQ SCAN 而不是INDEX SCAN

    【讨论】:

    • 请解释一下query analyze是什么。
    • “过滤子查询不使用索引” - 仅当过滤器表达式超出子查询时。
    • @user1032531 查询分析是EXPLAIN 我的东西你熟悉吗?
    【解决方案5】:

    优化查询运行时的步骤。您应该在每个步骤之后检查查询的速度 - 如果您可以在特定步骤中对其进行任何更改。:

    1. 总体上看一下您的查询,并尝试确认它只查询应该查询的内容。寻找未使用的字段、不必要的连接、不必要的外部连接。考虑使用 limit 来限制返回的记录数。请记住,组装比需要更大的结果集还需要额外的时间来创建并发送给客户端。
    2. 现在,再次仔细查看您的查询,看看您是否可以简化它。例如,您可能在选择列表中有子查询,您可以尝试将其转换为派生表。另请查看您的 where 条件并确认它们是否可以使用索引(表达式,例如 '%xxx%'。如果不能,请检查是否可以将它们更改为可以使用索引的 sg。
    3. 如果您在任何受影响的表上有任何索引,是时候使用analyse table 命令刷新它们,以确保安全。检查现有索引的基数。 Mysql 不太可能使用低基数的索引。如果基数与您认为的值相差甚远(给定字段中唯一值的数量),那么您可能需要调整 mysql 如何对数据进行采样以计算基数。
    4. 运行解释并检查是否
      • 在您期望的地方使用索引(可能的键、使用的键)
      • 避免连接类型 ALL 和文件排序

    尝试将索引添加到查询中未使用的部分,或者如果您认为自己已经有索引,然后使用索引提示,例如 force index 让 mysql 使用您的索引。

    1. 如果查询仍然很慢,那么您可能需要调整服务器端变量、使用不同的表引擎、对表进行分区、更改数据结构(非规范化)、归档旧数据以减小大小等。

    您可以为每个单独的步骤写长篇文章,或者在没有的情况下。 5、关于每一项。

    【讨论】:

      猜你喜欢
      • 2012-10-14
      • 1970-01-01
      • 1970-01-01
      • 2022-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多