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

MySQL 的 events_statements_summary_by_digest 表中,digest_text 是对原始 SQL 语句做标准化(normalized)后的模板形式,用于将语义相同、仅参数不同的查询归为一类。理解它的归类逻辑,是做慢查询分析、高频 SQL 识别和性能优化的关键。
digest_text 是怎么生成的?
MySQL 内部通过以下步骤生成 digest:
- 去除多余空格、换行、制表符,统一空白符为单个空格
- 将所有字面量(如字符串、数字、NULL)替换为 ? 占位符
- 忽略注释(包括 /* */ 和 -- 后内容)
- 统一关键字大小写(通常转为小写)
- 标准化函数名、操作符写法(如
IN和in视为相同)
例如: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.idvsSELECT user.id),或库名前缀有无(db1.t1vst1) -
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_text的count_star很低(如总是 1),且digest_text中仍有具体值(比如没被 ? 替换),说明应用没使用预编译,存在 SQL 注入风险和计划缓存浪费 -
比对执行计划一致性:对同一个 digest,检查
sum_select_full_join或sum_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 做了更友好的聚合展示,可直接作为分析入口










