监控脚本失效是因为mysql 8.0.30+默认关闭end_markers_in_json,导致optimizer_trace的trace字段缺失根大括号而无法被json_extract或jq解析,需在每次查询前显式set session end_markers_in_json = on并校验missing_bytes_beyond_max_mem_size。

监控脚本失效不是因为JSON格式“变”了,而是因为MySQL 8.0.30+默认关闭end_markers_in_json,导致JSON结构不合法、JSON_EXTRACT或jq解析直接报错。
为什么升级后information_schema.optimizer_trace查出来是空或解析失败
旧脚本常假设TRACE字段返回的是完整、可直解的JSON字符串,但MySQL 8.0.30起将end_markers_in_json默认设为off——这意味着输出的JSON没有根大括号{}包裹,也没有逗号分隔多个对象,而是一段无结构的嵌套片段。例如原本能解析的:
{"steps": [...], "chosen": true}
现在可能变成:
"steps": [...], "chosen": true
这会让JSON_EXTRACT(trace, '$.steps')返回NULL,或jq '.steps'报parse error。
- 必须在执行目标SQL前显式开启:
SET SESSION end_markers_in_json = ON - 仅设
optimizer_trace = "enabled=on"不够,这个开关独立控制JSON封装行为 - 若脚本用
SELECT ... INTO @trace再处理,需确认@trace变量接收的是字符串而非截断/乱码(尤其当MISSING_BYTES_BEYOND_MAX_MEM_SIZE > 0时)
如何让监控脚本兼容新老版本MySQL
不能硬编码依赖某版本的默认值。应在脚本初始化阶段动态检测并设置:
- 先查当前值:
SELECT @@end_markers_in_json;若为OFF,立即执行SET SESSION end_markers_in_json = ON - 同时检查
optimizer_trace_max_mem_size是否足够——子查询多的语句容易触发截断,建议统一设为4194304(4MB) - 避免用
SELECT * FROM information_schema.optimizer_trace取全部字段:只选TRACE和MISSING_BYTES_BEYOND_MAX_MEM_SIZE,前者用于解析,后者用于判断结果是否可信 - 若发现
MISSING_BYTES_BEYOND_MAX_MEM_SIZE > 0,该条trace应被丢弃,而不是强行解析残缺JSON
jq或Python解析时绕不开的细节
即使JSON结构完整,字段路径也因MySQL小版本略有差异。别写死.steps[0].join_optimization这种深度路径:
- 用
jq 'if .steps then .steps[] | select(has("join_optimization")) | .join_optimization else empty end'做安全遍历 - Python中用
json.loads(trace_str)前,先用trace_str.strip().startswith('{')校验格式 -
TRACE字段内容是JSON字符串,不是JSON对象——别漏掉json.loads()这一层 - 某些版本(如Aurora MySQL 3.02)会在
TRACE里混入\u0000空字符,需提前.replace('\x00', '')
最麻烦的点不在语法,而在状态隔离:监控脚本如果复用连接池,SET SESSION的配置可能被其他任务覆盖。每次取trace前,都得确保end_markers_in_json和optimizer_trace_max_mem_size是预期值,不能只设一次就不管。











