SQL MySQL 的 events_statements_summary 的 digest_text 归类分析

1次阅读

digest_text通过标准化生成:统一空白符、字面量转?、去除注释、关键字小写、函数/操作符归一化;表名列名、别名、库前缀、sql结构(如in占位符数量)、语义不同的函数/操作符会导致不同digest。

SQL MySQL 的 events_statements_summary 的 digest_text 归类分析

mysqlevents_statements_summary_by_digest 表中,digest_text 是对原始 SQL 语句做标准化(normalized)后的模板形式,用于将语义相同、仅参数不同的查询归为一类。理解它的归类逻辑,是做慢查询分析、高频 SQL 识别和性能优化的关键。

digest_text 是怎么生成的?

MySQL 内部通过以下步骤生成 digest:

  • 去除多余空格、换行、制表符,统一空白符为单个空格
  • 将所有字面量(如字符串、数字、NULL)替换为 ? 占位符
  • 忽略注释(包括 /* */ 和 — 后内容)
  • 统一关键字大小写(通常转为小写)
  • 标准化函数名、操作符写法(如 INin 视为相同)

例如:
select id, name FROM users WHERE age > 25 AND status = 'active';
→ 归一化为:
SELECT id , name FROM users WHERE age > ? AND status = ?

哪些差异会导致不同的 digest_text?

看似相似的 SQL,若以下任一条件不同,就会产生不同 digest:

  • 表名或列名不同:哪怕只是别名不同(SELECT u.id vs SELECT user.id),或库名前缀有无(db1.t1 vs t1
  • SQL 结构变化IN (1,2,3)IN (?, ?, ?) 是同一 digest;但 IN (1) 会生成 IN (?),与前者不同(占位符数量不同)
  • 函数或操作符语义不同:如 WHERE date(created) = '2024-01-01'WHERE created >= '2024-01-01' AND created 不会被归为一类
  • 隐式类型转换写法:如 WHERE id = '123'(字符串)和 WHERE id = 123(整数)在部分 MySQL 版本中可能生成不同 digest(取决于是否触发隐式转换处理路径)

如何有效利用 digest_text 做分析?

结合 events_statements_summary_by_digest 中的聚合指标,可定位典型问题:

  • 查高频低效语句:按 count_star 排序,找调用次数 Top N 的 digest,再看其 avg_timer_wait 是否偏高——说明“跑得勤但每次不快”,可能是缺少索引或走了全表扫描
  • 识未参数化的硬编码 SQL:如果某类 digest_textcount_star 很低(如总是 1),且 digest_text 中仍有具体值(比如没被 ? 替换),说明应用没使用预编译,存在 SQL 注入风险和计划缓存浪费
  • 比对执行计划一致性:对同一个 digest,检查 sum_select_full_joinsum_no_index_used 是否非零,确认是否每次执行都走相同低效路径
  • 关联原始语句调试:开启 performance_schema.setup_consumers 中的 events_statements_history_long,再按 digest 字段反查具体某次执行的完整 SQL 和绑定参数

注意事项和常见误区

digest 是性能分析的好工具,但不能完全替代人工判断:

  • digest_text 不保留注释,所以带 hint 的语句(如 /*+ USE_INDEX(t1, idx_a) */)会被抹掉,导致实际执行计划与 digest 显示不符
  • 分区表、临时表、CTE 等复杂结构可能导致 digest 归类粒度变粗或变细,需结合 object_schema/object_name 辅助判断
  • 开启 performance_schema 本身有约 5%~10% 性能开销,生产环境建议只在排查期启用,并控制 events_statements_history_long 容量
  • MySQL 8.0.14+ 支持 sys.statement_analysis 视图,它基于 digest 做了更友好的聚合展示,可直接作为分析入口
text=ZqhQzanResources