本教程从零构建一个名为 sql-index-doctor 的 Claude Code Agent Skill:把一段慢 SQL 或 EXPLAIN 执行计划丢给 Claude,它自动识别反模式(隐式类型转换、左模糊、函数包裹列、OR 短路、回表过多等),输出可执行的 CREATE INDEX 方案。学完你将掌握 Skill 的三大底层机制——模型自主调用(model-invoked)、渐进式披露(progressive disclosure)、allowed-tools 权限收口。适合已会用 Claude Code 基础交互、想封装团队级数据库优化经验的后端工程师与 DBA。
claude --version)EXPLAIN 输出(MySQL 5.7+/8.x 或 PostgreSQL 12+)mysql / psql 客户端用于回放验证在动手前,先理解 Skill 与你熟悉的「斜杠命令」的根本区别,否则会把 Skill 写成命令、把命令写成 Skill。
| 维度 | 斜杠命令(slash command) | Agent Skill |
|---|---|---|
| 触发方 | 用户手动键入 /xxx |
模型自主判断,依据 description |
| 加载时机 | 立即把 prompt 塞进上下文 | 先只加载 name+description,命中后才读全文 |
| 典型场景 | 固定流程、一次性指令 | 可复用的领域能力、按需激活 |
| 权限 | 走默认权限模型 | 可用 allowed-tools 精准收口 |
一句话:Skill 是「能力」,命令是「动作」。索引优化是典型能力——你不知道 Claude 什么时候会碰到慢查询,但你希望它一旦碰到就按 DBA 的方法论来。这就是 Skill 的主场。
Skill 的本质是一个带 SKILL.md 的目录,没有数据库、没有注册中心。先决定作用域:
~/.claude/skills/ —— 只对你生效,适合实验.claude/skills/ —— 随 git 共享给团队,适合沉淀规范我们把 SQL 索引医生放在项目级,因为索引规范要团队统一。
mkdir -p .claude/skills/sql-index-doctor/{scripts,templates}
cd .claude/skills/sql-index-doctor
touch SKILL.md scripts/explain_scan.py templates/report.md reference.md
最终目录结构(这一步是渐进式披露的物理基础):
.claude/skills/sql-index-doctor/
├── SKILL.md # 必需,总入口
├── reference.md # 可选,反模式手册(按需读取)
├── scripts/
│ └── explain_scan.py # 可选,EXPLAIN 解析器
└── templates/
└── report.md # 可选,输出模板
预期结果:ls .claude/skills/sql-index-doctor/ 能看到上述文件。此时 Claude 已经能发现这个 Skill,但还不会调用它,因为 SKILL.md 是空的。
常见问题:目录名必须与 name 字段一致吗?不强制,但强烈建议一致,便于排查。name 只允许小写字母、数字、连字符,最长 64 字符。
打开 SKILL.md,写入 YAML 头部。这是整个 Skill 最关键的部分:description 决定了 Claude 在什么情境下会想起你。
---
name: sql-index-doctor
description: |
MySQL/PostgreSQL 慢查询索引优化顾问。当用户做以下事情时使用本 Skill:
(1) 粘贴一段 SQL 问「为什么慢」「怎么优化」;(2) 提供 EXPLAIN 执行计划要解读;
(3) 排查索引缺失、回表过多、全表扫描;(4) 需要 CREATE INDEX 的可执行 DDL;
(5) 优化 WHERE / JOIN / ORDER BY / GROUP BY 的查询性能。
输出包含:命中反模式清单、EXPLAIN 关键字段判读、建议索引(含字段顺序与理由)、
覆盖索引机会、可执行 DDL、回滚方案。
allowed-tools: Read, Grep, Glob, Bash(mysql:*), Bash(psql:*)
---
操作说明——description 的三条铁律:
description 做语义匹配,触发词要覆盖用户的真实问法(「为什么慢」「EXPLAIN」「加索引」「全表扫描」)。allowed-tools 是 Claude Code 专属字段,用逗号分隔,支持通配(如 Bash(mysql:*) 表示只能跑以 mysql 开头的命令)。这里我们故意不放 Write/Edit——索引医生只读 SQL、只连库查 EXPLAIN,不该改业务代码。预期结果:保存后,在项目根目录问 Claude「What Skills are available?」,它会在列表里报出 sql-index-doctor 及其描述。
常见问题:Skill 不触发?九成是 description 没覆盖你的问法。比如用户说「这条 SQL 跑了 8 秒」,而你的描述里没有「慢」「耗时」「秒」这类触发词,模型就不会关联。修复:把用户的真实口语补进描述。
frontmatter 之下是 Markdown 正文,这是命中后 Claude 实际读到的「操作手册」。把一个资深 DBA 脑子里那套分析流程逐条写下来:
# SQL 索引医生
## 分析流程(严格按序执行)
1. **提取谓词**:从 WHERE / JOIN ON / ORDER BY / GROUP BY 中
抽出所有参与过滤、连接、排序、分组的列。
2. **识别反模式**:对照 reference.md 的反模式清单逐条扫描,
命中即记录(见下方「必须告警的反模式」)。
3. **判读执行计划**:若用户提供了 EXPLAIN,优先读取;
否则用 Bash 跑 `mysql -e "EXPLAIN <sql>"` 获取。
重点看 type/access_method、key、rows、Extra 四列。
4. **设计索引**:按「等值列在前、范围列在后、排序列收尾」
的原则组合联合索引,并判断能否做成覆盖索引。
5. **输出 DDL**:给出带注释的 CREATE INDEX 语句,
并说明每列为何在这个位置。
## 必须告警的反模式(命中即写入报告「风险」段)
- 隐式类型转换:varchar 列与数字比较(`where phone = 13800138000`)
- 左模糊:`where name like '%张'` 无法走索引
- 函数包裹列:`where date(create_time) = '2026-07-11'`
- OR 跨列短路:`where a=1 or b=2` 两列无索引则全表扫
- 深度分页:`limit 1000000, 20`
- 回表过多:SELECT * 但索引无法覆盖
## EXPLAIN 关键字段判读表
| 字段 | 危险值 | 含义 |
|---|---|---|
| type / access | ALL、index | 全表扫或全索引扫 |
| key | NULL | 没用上任何索引 |
| rows | 远大于实际结果集 | 估算扫描行过多 |
| Extra | Using filesort、Using temporary | 需额外排序/临时表 |
## 输出格式
严格按 templates/report.md 的结构输出,不要自由发挥段落顺序。
操作说明:正文要像给实习生写的 SOP——可执行、有顺序、有判定标准。避免「请智能分析」「综合考虑」这类模糊指令,它们会让输出不可控。
预期结果:此时 Skill 已可工作。粘贴一条慢 SQL,Claude 会按上述五步走,输出结构化报告。
常见问题:正文写多长合适?记住渐进式披露——SKILL.md 只在命中时加载一次,太长会挤占上下文。经验值:核心流程 50–150 行,细节外移到 reference.md。
第 3 步提到「对照 reference.md 的反模式清单」,现在把它写出来。这份文件默认不进上下文,只有 Claude 真的需要细节时才读取——这就是渐进式披露省 token 的关键。
# 索引反模式详解(reference.md)
## 反模式 1:隐式类型转换
**现象**:列是 varchar,查询写成数字字面量。
**示例**:`SELECT * FROM users WHERE phone = 13800138000`
**危害**:MySQL 会对 phone 列逐行套用类型转换函数,
索引失效,退化为全表扫描。
**修复**:`WHERE phone = '13800138000'`(加引号)
## 反模式 2:函数包裹索引列
**现象**:对列施加函数后比较。
**示例**:`WHERE DATE(create_time) = '2026-07-11'`
**修复**:改为范围 `WHERE create_time >= '2026-07-11'
AND create_time < '2026-07-12'`
## 反模式 3:左模糊
**现象**:`LIKE '%关键词'`
**说明**:B+Tree 索引按左前缀排序,左模糊无法定位。
若必须全文检索,建议引入全文索引或 ES。
## 反模式 4:OR 跨无索引列
**修复**:为 OR 两侧列各建单列索引,优化器会用
index_merge;或改写为 UNION ALL。
## 反模式 5:深度分页
**现象**:`LIMIT 1000000, 20`
**修复**:游标分页 `WHERE id > ? ORDER BY id LIMIT 20`
操作说明:reference.md 与 SKILL.md 的分工是——SKILL.md 说「做什么、按什么顺序」,reference.md 说「细节怎么判断、为什么」。前者是流程,后者是知识库。
预期结果:Claude 在分析时,遇到「函数包裹列」这类需要展开解释的情况,会主动 Read reference.md,而平时不加载它。
常见问题:怎么知道 Claude 有没有读 reference.md?开 /cost 或看工具调用日志,会出现一次 Read(.claude/skills/sql-index-doctor/reference.md)。
模型擅长语义分析,但数行数、做统计这种确定性任务交给脚本更准。我们写一个 Python 脚本,吃进 EXPLAIN 的 TSV 输出,吐出风险摘要。SKILL.md 通过 Bash 调用它。
# scripts/explain_scan.py
# 用法: mysql -e "EXPLAIN <sql>" -B | python3 explain_scan.py
import sys, csv, json
RISKY_TYPE = {"ALL", "index"} # 全表扫 / 全索引扫
RISKY_EXTRA = ["Using filesort", "Using temporary"]
rows = list(csv.DictReader(sys.stdin, delimiter="\t"))
findings = []
total_rows = 0
for r in rows:
access = r.get("type") or r.get("access_method") or ""
key = (r.get("key") or "").strip()
est = int(r.get("rows", 0) or 0)
extra = r.get("Extra", "")
total_rows += est
if access in RISKY_TYPE:
findings.append(f"行{r.get('id')}: 访问方式 {access}(疑似全扫)")
if not key and access not in ("const", "system"):
findings.append(f"行{r.get('id')}: 未命中任何索引")
for kw in RISKY_EXTRA:
if kw in extra:
findings.append(f"行{r.get('id')}: {kw}")
print(json.dumps({
"estimated_total_rows": total_rows,
"risk_count": len(findings),
"risks": findings or ["未发现明显执行计划风险"]
}, ensure_ascii=False, indent=2))
在 SKILL.md 正文里补一句调用方式:
当用户提供了 EXPLAIN 原始输出(TSV/表格)时,
先把它存为临时文件,再执行:
python3 scripts/explain_scan.py < explain.tsv
拿到结构化风险摘要后,再结合反模式给出索引建议。
操作说明:Skill 目录里的脚本用相对路径引用(相对于 Skill 根目录),Claude 会自动在正确的工作目录下运行。
预期结果:喂一段含全表扫的 EXPLAIN,脚本输出 JSON 摘要,Claude 基于摘要给出索引方案。
常见问题:allowed-tools 里只放了 Bash(mysql:*) 和 Bash(psql:*),跑 python3 会不会被拦?会。脚本要靠 mysql -e "EXPLAIN" 管道喂入,所以实际执行的是 mysql 命令;若必须单独跑 python,把 Bash(python3:*) 加进 allowed-tools——但加上等于放开任意脚本执行,按需权衡。
索引建议如果格式飘忽,团队就很难批量复用。写一个输出模板,让每次报告结构一致:
<!-- templates/report.md -->
## 🩺 索引诊断报告
### 1. 查询概要
- 数据库类型:MySQL / PostgreSQL
- 涉及表:
- 扫描行数(EXPLAIN 估算):
### 2. 命中反模式
| 反模式 | 位置 | 严重度 | 修复建议 |
|---|---|---|---|
### 3. 索引方案
```sql
-- 建议索引(等值列 → 范围列 → 排序列)
CREATE INDEX idx_xxx ON tbl (col_a, col_b, col_c);
列顺序理由:
能否把 SELECT 的列也纳入索引,避免回表:
EXPLAIN 应出现 type=ref/range、rows 显著下降DROP INDEX idx_xxx ON tbl;**操作说明**:在 SKILL.md 正文末尾加一句「输出格式严格遵循 templates/report.md」。这样 Claude 会先读模板再填空,而不是自由发挥。
### 第 7 步:测试、调优、上线
Skill 不是写完就灵,要像调 prompt 一样迭代 `description`。
**正向测试**(应当触发):
- 「这条 SQL 跑了 6 秒,`SELECT * FROM orders WHERE user_id=88 AND status=1 ORDER BY create_time DESC`」
- 「帮我看看这个 EXPLAIN,rows 怎么这么大」
**负向测试**(不应误触发):
- 「帮我写一个建表语句」——这是建表,不是优化,应触发建表类 Skill 而非本 Skill
- 「解释一下 B+Tree 原理」——这是科普,不需要出 DDL
**调优动作**:若负向误触发,在 `description` 里加**排除条件**,例如「仅用于已存在的查询的性能优化,不用于新建表结构设计」。
**预期结果**:正向命中、负向不命中,`/cost` 显示本 Skill 仅在相关对话被加载。
---
## 完整的 SKILL.md 总览
把上面几步拼起来,最终文件如下(可直接复制使用):
````markdown
---
name: sql-index-doctor
description: |
MySQL/PostgreSQL 慢查询索引优化顾问。当用户做以下事情时使用本 Skill:
(1) 粘贴一段 SQL 问「为什么慢」「怎么优化」;(2) 提供 EXPLAIN 执行计划要解读;
(3) 排查索引缺失、回表过多、全表扫描;(4) 需要 CREATE INDEX 的可执行 DDL;
(5) 优化 WHERE / JOIN / ORDER BY / GROUP BY 的查询性能。
不用于新建表结构设计。输出包含反模式清单、EXPLAIN 判读、建议索引、覆盖索引机会、DDL、回滚方案。
allowed-tools: Read, Grep, Glob, Bash(mysql:*), Bash(psql:*)
---
# SQL 索引医生
## 分析流程(严格按序)
1. 提取谓词:WHERE / JOIN ON / ORDER BY / GROUP BY 的列
2. 识别反模式:对照 reference.md 逐条扫描
3. 判读执行计划:用户提供则读,否则 `mysql -e "EXPLAIN <sql>"`;
有 TSV 原始输出时先跑 `python3 scripts/explain_scan.py < explain.tsv`
4. 设计索引:等值列在前、范围列在后、排序列收尾
5. 输出 DDL:带注释的 CREATE INDEX + 列顺序理由
## 必须告警的反模式
隐式类型转换 / 左模糊 / 函数包裹列 / OR 跨列短路 / 深度分页 / 回表过多
## EXPLAIN 判读
- type/access 为 ALL/index → 全扫风险
- key 为 NULL → 未命中索引
- rows 远大于结果集 → 扫描过多
- Extra 含 Using filesort/Using temporary → 额外排序
## 输出格式
严格遵循 templates/report.md。细节判断依据见 reference.md。
````
---
## 实战案例:救活一条 6 秒的订单查询
### 现场还原
某电商后台一条订单查询,数据量 800 万行,稳定跑 6 秒:
```sql
SELECT id, order_no, user_id, amount, status, create_time
FROM orders
WHERE user_id = 88
AND status = 1
AND DATE(create_time) = '2026-07-11'
ORDER BY create_time DESC
LIMIT 20;
第 1 步——提取谓词:user_id(等值)、status(等值)、create_time(函数包裹)、create_time(排序)。
第 2 步——识别反模式:命中「函数包裹列」——DATE(create_time) = '2026-07-11',索引会失效。
第 3 步——改写 + 设计索引。先消除反模式,再建索引:
改写后的等价查询(去掉函数):
WHERE user_id = 88 AND status = 1
AND create_time >= '2026-07-11 00:00:00'
AND create_time < '2026-07-12 00:00:00'
第 4 步——索引方案:
-- 联合索引:等值列(user_id, status) 在前,
-- 范围列(create_time) 在后,同时支撑 ORDER BY,避免 filesort
CREATE INDEX idx_uid_status_time ON orders (user_id, status, create_time);
列顺序理由:user_id 选择性最高放最左;status 等值过滤放第二;create_time 既是范围又是排序列放最后,可同时满足过滤与 ORDER BY,消除 Using filesort。
第 5 步——覆盖索引评估:SELECT 列含 order_no、amount,若全部塞进索引会让索引臃肿。本例回表 20 次(LIMIT 20)成本可接受,不强行做覆盖索引,权衡得当。
| 反模式 | 位置 | 严重度 | 修复 |
|---|---|---|---|
| 函数包裹列 | DATE(create_time) |
高 | 改范围查询 |
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 访问方式 | ALL(全表扫) | ref |
| 估算扫描行 | ~8,000,000 | ~50 |
| Extra | Using filesort | (无) |
| 耗时 | 6.0s | 0.02s |
DROP INDEX idx_uid_status_time ON orders;
这个案例完整演示了 Skill 的价值:把一位 DBA 的判断流程,固化成团队任何人都能调用的能力,且每次输出结构一致、可审计、可回滚。
flowchart TD
A[用户粘贴慢SQL/EXPLAIN] --> B{Claude语义匹配<br/>description?}
B -->|命中| C[加载 SKILL.md 全文]
B -->|不命中| Z[走普通对话]
C --> D[allowed-tools 收口权限]
D --> E[1.提取谓词]
E --> F[2.对照 reference.md 扫反模式]
F --> G{用户提供EXPLAIN?}
G -->|是| H[跑 scripts/explain_scan.py]
G -->|否| I[Bash: mysql -e EXPLAIN]
H --> J[3.结构化风险摘要]
I --> J
J --> K[4.设计联合索引<br/>等值→范围→排序]
K --> L[5.评估覆盖索引]
L --> M[按 templates/report.md 输出]
M --> N[给出DDL + 验证 + 回滚]
scripts/ 下的 Python,模型只做语义判断与方案设计。脚本可单测、可版本化,比纯 prompt 更可靠。reference.md,新人换项目也能继承。https://code.claude.com/docs/en/skillshttps://docs.claude.com/en/docs/agents-and-tools/agent-skills/best-practiceshttps://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skillshttps://mikhail.io/2025/10/claude-code-skills/description;② 能否合理用 allowed-tools 收口权限;③ 能否用脚本 + reference.md 践行渐进式披露;④ 输出报告是否结构一致、可审计。-p 批处理模式与本 Skill)。allowed-tools、渐进式披露的权威说明name/description/allowed-tools 各字段的逐项释义与校验规则[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
