OceanBase 慢 SQL 执行计划速诊
把一段 EXPLAIN 文本变成「看得懂的算子表 + 明确的风险清单」,让排查有据可依,而不是凭直觉加索引。
何时使用
- 某条 SQL 变慢,需要判断瓶颈在扫描、连接、排序还是回表。
- 上线前评审新 SQL,确认没有全表扫描或隐式排序。
- 需要向他人解释一个执行计划,但输出太宽、看不清层次。
不适用:拿不到执行计划文本、只有耗时数字;或者问题出在锁等待、连接数、磁盘 IO 等非计划层面——此时应先排除这些因素。
输入准备
优先使用实际计划文本,不要臆造。
- 实时计划(OceanBase MySQL 模式):
EXPLAIN <你的 SQL>; - 历史计划:先在
oceanbase.GV$OB_SQL_AUDIT里按query_sql、时间区间定位目标 SQL 与trace_id,再展开该 trace 的计划。不同大版本的审计视图字段存在差异,以当前实例为准,不要假定字段名。 - 命令行工具
obdiag也能采集计划,适合生产环境不便手工执行 EXPLAIN 的情况。
拿到文本后另存为 plan.txt,注意保留原始的竖线表格与缩进——缩进是算子层级的唯一线索。
三步流程
- 生成摘要:
python scripts/explain_digest.py plan.txt - 逐条核对风险提示,剔除误报:估算行数偏大不一定代表真实扫描量大,需结合
range、filter与实际数据分布确认。 - 按建议动作给出结论:加索引、改写条件、拆分查询、或调整连接顺序。
风险判读要点
| 现象 | 含义 | 动作 |
| --- | --- | --- |
| TABLE SCAN 且 range: [] | 没有可用的索引范围,扫描整表 | 确认过滤列是否有可用索引,注意前导列与隐式类型转换 |
| is_index_back=true | 走索引后仍需回表取列 | 把查询列加入索引形成覆盖索引,或减少回表行数 |
| SORT 算子 | 排序无法借力索引 | 让 ORDER BY 列与索引顺序一致,避免在函数包裹的列上排序 |
| MATERIAL / 临时结果 | 中间结果被物化 | 检查子查询是否能下推,或拆分批次 |
| 估算行数与实际差一个数量级以上 | 统计信息失真 | 更新统计信息后再评估计划 |
补充:range: [] 只是「无范围」的常见写法,实际输出可能带空格或写作 range:(NULL),以脚本提示为准,人工复核一次。
输出要求
- 先给算子表(按层级顺序,不要重排),再给风险清单,最后给建议。
- 风险分级只用「高 / 中 / 低」,每条都必须指向具体算子编号。
- 建议必须可执行,例如「在
(tenant_id, created_at)上建联合索引」,而不是「优化索引」。 - 不确定的地方直接说明不确定,不要编造执行计划里没有的算子。
边界
- 不做在线变更:脚本只读文本,不连接数据库,不执行 DDL。
- 不替代压测:计划好不等于快,仍需在目标数据量下验证。
Scan to join WeChat group