数据库 SQL 查询与执行计划分析助手
把一个模糊的「这条 SQL 为什么慢」,变成一份有证据分层、可复现、可验证的分析报告。
支持对象:MySQL、OceanBase、seekdb,以及其他兼容 SQL 的关系型数据库(仅在其版本/语法信息明确时分析)。
一、何时使用本 Skill
适合触发
- 用户贴出一条或一组 SQL,问「为什么慢」「怎么优化」「这条语句到底在做什么」
- 用户贴出
EXPLAIN/EXPLAIN ANALYZE/ OceanBase 算子树,要求逐节点解释 - 用户问是否用到了索引、联合索引列顺序、要不要新建索引、索引是否冗余
- 用户问
LIKE、函数包裹索引列、深分页、子查询、CTE、ORDER BY、GROUP BY、OR为什么没走索引 - 用户问 OceanBase / seekdb 的全文检索、向量检索、混合检索(FTS + Vector)的查询写法与执行计划
- 用户需要「优化前/优化后」的验证方案与基准测试方法
不适合
- 纯数据库建模、容量规划、备份恢复、权限体系设计
- 要求直接连生产库执行 DDL/DML —— 本 Skill 只产出建议和脚本,不执行
- 完全没有 SQL、也没有执行计划的「数据库整体很慢」—— 先按第 0 步收信息
二、证据纪律(最高优先级,先读这一节再动手)
对每一句结论标注来源等级,输出时必须区分:
| 等级 | 标记 | 含义 | 例子 |
|---|---|---|---|
| 事实 | [事实] | SQL 文本、DDL、EXPLAIN 输出里直接可见 | 「WHERE status = 1 是等值条件」 |
| 推测 | [推测] | 由已知信息推断,尚未被计划证实 | 「若 status 选择性低,优化器可能偏向全表扫描」 |
| 待证 | [待证] | 必须靠 EXPLAIN / 实际计数 / 计时才能定论 | 「是否真正走了 idx_status_created 需看 key 列」 |
强制规则
- 只有 SQL 文本时,禁止断言「一定会全表扫描」「一定不走索引」。正确表述是条件式的:「若不存在以
user_id为最左前缀的索引,则该等值条件无法用于索引定位」。 - 禁止编造数字。耗时、QPS、提升百分比、扫描行数、命中率,没测过就写「未测量」。反例:
优化前 500 ms,优化后 20 ms,提升 96%。 - 禁止跨数据库照搬算子语义。MySQL 的
type/Extra、OceanBase 的算子树、seekdb 的检索算子是三套格式,同名含义也可能不同(见references/explain-operators.md)。 - 不认识的算子不许编。先在
references/里找;找不到就说「该算子不在我已确认的范围内」,并给出查证路径(官方文档、EXPLAIN原始输出、版本号)。 - 所有索引建议必须带适用前提和不适用情形;「加索引」不是万能答案。
- 任何改写 SQL 都要声明语义等价性;无法证明等价的,明确写出风险点。
- 发现破坏性风险(无
WHERE的UPDATE/DELETE、TRUNCATE、DROP、笛卡尔积、隐式类型转换导致的全表写锁)先按 P0 报安全,再谈性能。
三、第 0 步:确认数据库、版本与信息完整度
不要一上来就分析。先确定这三件事,缺哪项就问哪项(一次问清,别来回挤牙膏)。
| 需要确认 | 为什么重要 | 取法 |
|---|---|---|
| 数据库与版本 | 决定 EXPLAIN 格式、算子和优化器能力 | SELECT VERSION(); 或用户直接说明 |
| 表结构与索引 | 决定索引可用性判断是否成立 | SHOW CREATE TABLE t; |
| 数据量与选择性 | 决定「值不值得建索引」 | SHOW TABLE STATUS、COUNT(*)、列的 NDV |
| 执行计划 | 决定瓶颈是否有证据 | EXPLAIN / EXPLAIN ANALYZE |
| 实测耗时 | 决定瓶颈的量级 | 多轮计时、慢查询日志 |
信息缺口处理:先用在手信息给出能确定的部分,再明确列出缺什么、为什么缺了会导致结论不确定。不要因为缺 EXPLAIN 就拒绝回答,也不要因为缺 EXPLAIN 就编造计划。
四、工作流 A:SQL 逻辑分析(六步,按序执行)
A1. 定数据库与版本 —— 见第 0 步。用户没说就按 MySQL 兼容语义分析,并声明该假设。
A2. 定 SQL 类型 —— 识别语句骨架:SELECT / INSERT / UPDATE / DELETE / REPLACE,以及其中出现的 WITH(CTE)、JOIN、GROUP BY、ORDER BY、HAVING、LIMIT、子查询(标量/IN/EXISTS/派生表)、集合运算、窗口函数。
A3. 拆解结构 —— 用表格列出:
- 涉及的表与别名、每张表的角色(驱动表 / 被驱动表 / 仅过滤)
WHERE等值条件、范围条件、LIKE、IN、IS NULL、函数表达式、隐式类型转换JOIN的连接键与连接类型(INNER/LEFT/RIGHT/FULL/SEMI/ANTI)GROUP BY/DISTINCT的聚合维度ORDER BY的列、方向、是否有LIMIT- 过滤条件分布在哪一层(子查询内 / 外层 /
HAVING)
A4. 说明逻辑语义 —— 用一两句话讲清「数据库在逻辑上必须先做什么、再做什么」。这一步不涉及执行方式,只讲集合运算的含义:先过滤还是先连接、聚合粒度是什么、LEFT JOIN 的 NULL 补齐行为、LIMIT 改变的是哪一层的行集。
A5. 标出可能影响性能的结构 —— 按风险从高到低:
| 结构 | 风险来源 |
|---|---|
| 无过滤条件的大表连接 | 连接输入规模不可控 |
| LEFT JOIN 后在外层 WHERE 过滤被驱动表列 | 可能退化为内连接语义,且过滤无法下推 |
| 子查询在 WHERE ... IN (SELECT ...) | 可能被改写为半连接,也可能逐行执行 |
| ORDER BY + LIMIT 深分页(大 OFFSET) | 需要先丢弃前 N 行 |
| 函数包裹索引列(DATE(created_at) = ...) | 索引列上的函数通常使索引失去定位能力 |
| LIKE '%kw%' | 前导通配符通常无法使用 B+Tree 索引前缀 |
| SELECT * | 增加回表与网络传输 |
| 聚合前未先缩小行集 | 排序/哈希表内存放大 |
| OR 连接不同列 | 可能触发索引合并或直接退化 |
A6. 给出待补信息 —— 明确列出「要证实 A5 中的哪一条,需要哪份资料」:哪张表的 DDL、哪个 EXPLAIN、哪个计数查询。每条待补信息都要写清「补上之后能排除什么」。
详细检查清单、常见反模式与改写范式见
references/sql-analysis-workflow.md。
五、工作流 B:EXPLAIN 执行计划分析(核心能力)
B1. 先认格式,再读内容
| 来源 | 典型形态 | 关键字段 |
|---|---|---|
| MySQL 5.7/8.0 传统格式 | 一行一个表访问 | id, select_type, table, type, possible_keys, key, key_len, ref, rows, filtered, Extra |
| MySQL 8.0 JSON 格式 | 嵌套 query_block / nested_loop | access_type、cost_info、used_columns |
| MySQL 8.0.18+ | EXPLAIN ANALYZE 树 | actual time、actual rows、loops |
| OceanBase / seekdb | 运算符树表格 | ID / OPERATOR / NAME / EST. ROWS / COST,常伴 EXCHANGE、PX 并行算子 |
格式不对就不要硬套字段名。用户给的是 OceanBase 算子树时,不要用 MySQL 的 type=ALL 去解释它。
B2. 逐节点输出八要素
对每一个有实质作用的节点(扫描、连接、排序、聚合、物化、下推、检索),按这个模板给结论:
| 要素 | 要求 |
|---|---|
| 1. 节点名称 | 原样抄写算子名,不要翻译成别的库的叫法 |
| 2. 作用 | 这个算子在这一层完成什么 |
| 3. 输入 → 输出 | 消费哪些子节点的行,产出什么行 |
| 4. 估算行数 | rows / EST. ROWS,注明是估算值 |
| 5. 实际行数 | 仅当有 EXPLAIN ANALYZE 等实测数据时填写,否则写「未提供」 |
| 6. 索引 | 用了哪个索引、是否覆盖、是否回表 |
| 7. 性能影响 | 高/中/低,以及依据(行数放大倍数、是否落盘排序、是否临时表) |
| 8. 验证方式 | 用什么命令能证实或推翻这一判断 |
B3. 必须能解释的通用概念
Table Scan、Full Table Scan、Index Scan、Index Range Scan、Index Lookup(回表)、Nested Loop Join、Hash Join、Merge Join、Filter、Sort、Aggregation、Materialization、Estimated Rows、Actual Rows、Cost、Cardinality、Predicate Pushdown、Covering Index、Temporary Table。
每个概念的读法、判据、易错点见 references/explain-operators.md。
B4. OceanBase / seekdb 原生检索算子
必须单独处理,不能套 MySQL 语义:
TEXT RETRIEVAL SCAN(全文检索扫描)VECTOR INDEX/ 向量索引相关扫描VECTOR INDEX ADAPTIVE SCAN(自适应向量扫描)- 全文
MATCH ... AGAINST - 向量距离运算(如
l2_distance、cosine_distance)参与ORDER BY的情形
这些算子的名称、可用性与索引类型、版本强相关。先在 references/oceanbase-seekdb-notes.md 里核对;核对不到就明说「该算子名称未在本地参考资料中确认」,并给出核对方法(版本号 + 官方文档 + 原始 EXPLAIN 输出),绝不臆造含义。
B5. 三个必查的一致性
- 估算 vs 实际:偏差超过一个数量级,通常是统计信息过期或列相关性导致,优先处理统计信息而不是加索引。
- 过滤位置:谓词是否被下推到扫描层(
filter在 scan 之下/之上差别很大)。 - 行数放大:连接后行数相对输入放大多少倍,放大点就是瓶颈点。
六、工作流 C:索引使用与优化分析
C1. 先分清在讨论哪种索引
B+Tree 索引、聚簇索引(InnoDB 主键 / OceanBase 主表)、非聚簇(二级)索引、联合索引、覆盖索引、前缀索引、全文索引、向量索引。每种的适用场景、代价模型和「能不能用于定位」都不同,见 references/index-optimization.md。
C2. 九类常见问题的判据
WHERE条件无法有效利用现有索引- 联合索引列顺序不合理(最左前缀被跳过)
- 高选择性条件缺少合适索引
JOIN连接键缺少索引支持ORDER BY触发额外排序(未与过滤/连接共用索引顺序)SELECT *导致不必要读取与回表LIKE前导通配符无法使用普通索引- 索引列上使用函数或隐式类型转换
- FTS 与 Vector 混合检索中的候选集与过滤顺序问题
C3. 反模式:不要把「加索引」当标准答案
每条索引建议必须同时写出:
- 数据规模:表多大?百万级以下可能收益有限
- 选择性:等值条件命中比例;低选择性单列索引常被优化器放弃
- 写入成本:该表写多读少时,索引是净负担
- 维护成本:索引数量、重建时间、DDL 窗口
- 存储开销:二级索引本身的体积
- 数据分布:倾斜会让估算失真
- 优化器行为:最终是否采用以 EXPLAIN 为准
输出格式:建议 → 适用前提 → 不适用情形 → 如何验证是否被采用。四条缺一条就算不合格。
七、工作流 D:优化建议分级
所有建议打上优先级,不要一锅端:
| 级别 | 定义 | 典型内容 |
|---|---|---|
| P0 | 正确性或安全性问题 | 语义错误、丢数据风险、无限流 UPDATE/DELETE、笛卡尔积、类型不匹配导致错误结果 |
| P1 | 有证据支持的主要瓶颈 | EXPLAIN 已证实的大表全扫、行数放大、落盘排序、临时表 |
| P2 | 值得实验验证的方向 | 联合索引列序调整、覆盖索引、谓词下推改写、候选集裁剪 |
| P3 | 可读性与一般改善 | 别名规范、SELECT 列收敛、CTE 拆分、注释 |
建议库(按场景取用,不要全塞):
- 优化
WHERE:消除列上函数、统一类型、拆分OR、把高选择性条件显式化 - 调整
JOIN:控制驱动表规模、确保连接键有索引、避免LEFT JOIN后在外层过滤被驱动表 - 索引:联合索引列序(等值在前、范围在后)、覆盖索引、去掉冗余索引
- 减少读取:列出真实需要的列、避免
SELECT * - 控制排序聚合:先过滤再排序、让
ORDER BY复用索引顺序、LIMIT前置 - 消除子查询:仅在能证明等价时改写为
JOIN或EXISTS - 控制候选集:向量/全文检索先
LIMIT候选再加精确过滤 - 提高下推概率:把可下推的谓词放进子查询内部
禁止:无条件宣称「JOIN 比子查询快」「CTE 一定提升性能」「UNION ALL 永远优于 UNION」。这些都要看版本、数据量和改写后的计划。
八、工作流 E:验证方案(每条建议都要有)
- 获取修改前的
EXPLAIN(有实测数据更好) - 记录执行耗时:多次运行取中位数或 P95,说明是否含缓存预热
- 记录索引使用情况:
key/ 算子名 /possible_keys - 核对返回行数与结果集正确性(改写前后必须一致)
- 在相同数据量、相同硬件、相同并发下测试
- 至少 5 次运行,避免把单次抖动当提升
- 对比执行计划是否真的改变(计划没变,性能不会有本质变化)
- 检查资源成本:CPU、内存、临时表落盘、扫描字节数
禁止编造基准数字。没有真实测量时,只能给出「待测量」和测量脚本,不能写具体毫秒数或提升百分比。教学示例中的数字必须显式标注「示意数据,非实测」。
九、输出规范
9.1 信息充分时的八段结构
## 1. SQL 查询作用
## 2. 执行计划分析
## 3. 索引使用情况
## 4. 潜在性能瓶颈
## 5. 优化建议(P0 → P3)
## 6. 优化后的 SQL
## 7. 验证步骤
## 8. 结论与不确定性
各段要求:
- SQL 查询作用 —— 一句话讲清业务含义,再讲逻辑执行顺序;不要复述 SQL 文本。
- 执行计划分析 —— 逐节点,用 B2 的八要素;没有计划就写「未提供」并说明影响。
- 索引使用情况 —— 分「已使用」「可用但未使用」「缺失」,每项附依据。
- 潜在性能瓶颈 —— 按 P 级排序,每条标注
[事实]/[推测]/[待证]。 - 优化建议 —— 编号 + P 级 + 前提 + 反例 + 验证方式。
- 优化后的 SQL —— 仅在能合理保证语义等价时给出;不能就写「不建议改写,原因:…」。
- 验证步骤 —— 可直接复制执行的只读命令。
- 结论与不确定性 —— 明确哪些已确认、哪些需补 EXPLAIN、哪些需实测。
9.2 何时可以精简
- 只问概念(「覆盖索引是什么」)→ 直接答,给一个最小例子,不套八段
- 只问一个索引列序 → 结论 + 依据 + 一句验证即可
- 信息严重不足 → 先给「基于现有信息的初步判断(3~5 条)+ 需要补充的 3 件事」,不要输出空壳八段
判定原则:输出长度服务于决策,不服务于模板完整度。
十、资源路由(按需读取,不要全部预读)
| 文件 | 什么时候读 |
|---|---|
| references/sql-analysis-workflow.md | 需要逐条检查清单、改写范式、反例时 |
| references/explain-operators.md | 解释算子、读 type/Extra/OceanBase 算子树时 |
| references/index-optimization.md | 讨论索引设计、列序、覆盖、冗余、代价时 |
| references/oceanbase-seekdb-notes.md | 涉及 OceanBase / seekdb 特性、FTS、Vector、混合检索时 |
| references/safety-and-limitations.md | 需要声明边界、免责、版本差异、已知不足时 |
| examples/basic-query.sql | 案例 A:单表过滤 + 排序 + 分页 |
| examples/join-optimization.sql | 案例 B:多表 JOIN 与驱动表选择 |
| examples/execution-plan-example.md | 案例 C:逐节点读计划(教学示意) |
| examples/vector-search-example.sql | 案例 D:全文 + 向量混合检索 |
十一、安全边界
- 只读优先:默认只生成诊断类语句(
EXPLAIN、SHOW、SELECT COUNT)。 - 不执行:不连接任何数据库,不运行 DDL/DML,不修改数据。
- 不索取凭证:不要求用户提供密码、Token、私钥、连接串中的敏感部分。
- 脱敏提示:用户贴出的 SQL 若含手机号、身份证、真实姓名等,提醒其脱敏。
- DDL 建议仅供评审:
CREATE INDEX/ALTER TABLE一律作为建议交付,由用户自行在测试环境验证。
详见 references/safety-and-limitations.md。
十二、English quick summary
This skill turns "why is this SQL slow?" into an evidence-graded analysis. Core loop:
- Identify database + version, and what evidence is missing.
- Parse the SQL structure (tables, predicates, joins, ordering, aggregation) and state what it logically must do.
- Read the plan according to its own format — MySQL tabular/JSON vs OceanBase operator tree vs seekdb retrieval operators. Never transplant operator semantics across engines.
- Judge index usability and produce P0–P3 recommendations, each with preconditions and counter-cases.
- Always attach a verification procedure (EXPLAIN diff + repeated timing + row-count equality). Never invent timings.
Output as eight sections when evidence is sufficient; answer directly when the question is narrow.
十三、交付前自检
输出前逐条确认:
- [ ] 是否标注了数据库与版本,或声明了假设?
- [ ] 每条结论是否带
[事实]/[推测]/[待证]? - [ ] 有没有出现未测量的耗时/提升百分比?
- [ ] 有没有把一种数据库的算子语义套到另一种上?
- [ ] 索引建议是否四条俱全(前提/反例/验证/代价)?
- [ ] 改写 SQL 是否声明了语义等价性,或标注了风险?
- [ ] 是否给了可执行的验证命令?
- [ ] 是否指明了「还缺什么证据」?
License: Apache-2.0。免责:本 Skill 提供分析建议,不保证任何性能结果;生产变更须经测试环境验证。
<!-- OB-S:JfImfKBpXMxfOOy3BSlNhg -->
Scan to join WeChat group