把慢查询日志扔给 AI,从分析到修复只用了半小时:一份完整的实操手册

面对 MySQL 慢查询,如何让 AI 帮你分析执行计划、生成索引建议和 SQL 改写方案,并验证效果。本文记录完整流程、提示词模板、踩坑复盘,以及与其他优化方式的真实对比。

背景:凌晨 3 点触发的慢查询告警

事情发生在上周三凌晨。我被手机告警震醒,打开一看:某支付系统的对账接口响应时间飙到 8 秒,数据库 CPU 直接打满。

定位到是一条看起来人畜无害的 SQL:

sql 复制代码
SELECT t.order_id, t.tx_id, t.amount, t.status, t.created_at,
       o.merchant_id, o.user_id,
       r.refund_amount, r.refund_reason
FROM transactions t
LEFT JOIN orders o ON t.order_id = o.id
LEFT JOIN refunds r ON t.order_id = r.order_id
WHERE t.status IN ('SUCCESS','FAILED','PROCESSING')
  AND t.created_at >= '2026-04-01'
  AND t.created_at < '2026-05-01'
ORDER BY t.created_at DESC
LIMIT 50;

EXPLAIN 结果让我倒吸一口气:

type rows Extra
transactions ALL 14,832,451 Using where; Using filesort
orders eq_ref 1
refunds ALL 6,241,783 Using where

transactions 表 1400 万行全表扫描,refunds 表 600 万行也没走索引。难怪跑不动。

搁在以前,我会手动翻文档、看索引、改 SQL、测试、上线,一套下来至少半天。这次我决定试试,能不能让 AI 帮着加速这个流程。


我的工作流:三步让 AI 参与进来

我用的工具组合:

  • ChatGPT (GPT-5.5):分析 EXPLAIN 结果,生成优化建议。
  • Cursor (Claude Sonnet 4 模型):在项目代码中改写 SQL,并生成验证脚本。
  • 终端 + MySQL 客户端:执行测试,验证性能。

整体节奏是:我分析 → 我提问 → AI 给方案 → 我验证 → 上线,AI 负责中间的"加速部分",决策还是我。

第一步:把诊断信息喂给 AI,让它看懂执行计划

执行计划看着简单,但到底缺什么索引,需要结合查询条件、数据分布来判断。我直接把 EXPLAIN 输出和表结构贴给了 ChatGPT,问了三个问题:

提示词原文

diff 复制代码
你是一位资深 MySQL DBA。下面是一条慢查询的 EXPLAIN 结果和表结构。
transactions 表约 1400 万行,orders 约 50 万行,refunds 约 600 万行。

## SQL
[上面的 SQL]

## EXPLAIN
[粘贴 EXPLAIN 输出]

## 表结构
transactions:
- id (PRIMARY)
- order_id (FK)
- tx_id (UNIQUE)
- status (ENUM 'SUCCESS','FAILED','PROCESSING','PENDING')
- amount
- created_at
- INDEX idx_created_at (created_at)
- INDEX idx_status (status)

orders:
- id (PRIMARY)
- ...

refunds:
- id (PRIMARY)
- order_id (FK)
- amount
- status
- reason
- INDEX idx_order_id (order_id)

请分析:
1. 为什么两个表走了全表扫描?
2. 建议创建哪些索引(给出精确的 DDL)?
3. SQL 本身是否可以改写以提升性能?

它给出的回复中,核心诊断让我有些意外,但也非常精准:

原因分析: transactions 表上虽然已有 idx_status 和 idx_created_at 两个单列索引,但查询中同时有 status IN(...)created_at 范围条件,优化器只能二选一。由于 status 过滤后仍有大量数据,最终选择全表扫描。

refunds 表使用了 idx_order_id,但该索引不包含查询需要的 refund_amountrefund_reason,导致回表开销极大,优化器认为还不如全表扫描。

这个分析让我省去了自己推演的时间,直接确认了根因:联合索引缺失

第二步:让 AI 给出精确的索引 DDL,但要自己验证

AI 接着给出了几条索引建议,其中最关键的是:

sql 复制代码
-- 建议 1:transactions 表的覆盖索引
ALTER TABLE transactions ADD INDEX idx_status_created (status, created_at, order_id, tx_id, amount);

-- 建议 2:refunds 表的覆盖索引
ALTER TABLE refunds ADD INDEX idx_order_id_covering (order_id, refund_amount, refund_reason);

我注意到它把 amounttx_id 也放进索引了,这是为了让索引"覆盖"查询所需的所有列,避免回表。

但这里我必须做的一件事:检查索引的大小和维护成本。AI 不会告诉你 1400 万行的表加一个 5 列索引需要锁多久、占用多少磁盘。我查了一下:

  • 索引大小预估:约 14M * (4+4+8+8+8) ≈ 450MB,可接受。
  • ONLINE DDL 时间:测试环境跑了一下,大约 3 分钟,生产环境是 PolarDB,可以用 ALGORITHM=INSTANT 秒级完成。

于是我略作调整,加上了 ALGORITHM=INSTANT(MySQL 8.0 支持):

sql 复制代码
ALTER TABLE transactions 
  ADD INDEX idx_status_created_cover (status, created_at, order_id, tx_id, amount),
  ALGORITHM=INSTANT;

AI 给了起点,但最终决策需要结合环境常识。这是我的第一条原则:AI 出方案,人做安全校验。

第三步:让 AI 改写 SQL,顺便检查业务逻辑

加了索引后,EXPLAIN 显示 rows 降到 28 万,但仍有些慢。AI 在第一步分析时还提到一点:

SQL 可改写:先利用覆盖索引快速拿到 50 个 order_id,再用这些 ID 去关联 orders 和 refunds,避免大表 JOIN 后排序。

这启发了我,我让 Cursor 帮我改写:

sql 复制代码
-- 优化后的写法:子查询 + 延迟关联
SELECT t.order_id, t.tx_id, t.amount, t.status, t.created_at,
       o.merchant_id, o.user_id,
       r.refund_amount, r.refund_reason
FROM (
    SELECT order_id, tx_id, amount, status, created_at
    FROM transactions
    WHERE status IN ('SUCCESS','FAILED','PROCESSING')
      AND created_at >= '2026-04-01'
      AND created_at < '2026-05-01'
    ORDER BY created_at DESC
    LIMIT 50
) t
LEFT JOIN orders o ON t.order_id = o.id
LEFT JOIN refunds r ON t.order_id = r.order_id
ORDER BY t.created_at DESC;

子查询里只取 transactions 表本身的列,可以利用覆盖索引直接完成排序和 LIMIT,不用回表。外层的 JOIN 只关联 50 行,refunds 表 600 万行也只扫描 50 行,成本极低。

新执行计划:

type rows Extra
transactions range 286,512 Using where; Using index
orders eq_ref 1
refunds ref 3 Using where

refunds 从全表扫描变成 ref,rows 从 600 万降到了 3。整个查询耗时从 8 秒降到了 120 毫秒

这个过程中我学到的 :SQL 改写不是炫技,而是根据数据分布和业务需求做取舍。AI 在这一点上给出了一个教科书式的"延迟关联"优化,但它并不知道我们业务中 refunds 是否会有重复。我确认了 order_id 在 refunds 表里是唯一的,这才放心用。

额外收获:AI 教了我怎么写"验证脚本"

优化完成后,我需要一个能对比优化前后性能的脚本,并确认结果集完全相同。我向 Cursor 描述需求,它生成了以下 Python 脚本:

python 复制代码
import pymysql
import time

def run_and_measure(cursor, sql, label):
    start = time.time()
    cursor.execute(sql)
    rows = cursor.fetchall()
    elapsed = time.time() - start
    print(f"[{label}] 返回 {len(rows)} 行,耗时 {elapsed:.4f} 秒")
    return rows, elapsed

conn = pymysql.connect(host='localhost', user='test', password='xxx', db='pay')
cur = conn.cursor()

# 原 SQL
old_sql = "..."  # 省略
# 优化后 SQL
new_sql = "..."  # 省略

old_rows, old_time = run_and_measure(cur, old_sql, "原SQL")
new_rows, new_time = run_and_measure(cur, new_sql, "优化后SQL")

# 验证结果集一致性(只比关键列,因为 LEFT JOIN 可能引入 NULL 差异)
old_set = {row[0] for row in old_rows}  # 只比 order_id
new_set = {row[0] for row in new_rows}
if old_set == new_set:
    print("✅ 结果集一致")
else:
    print("❌ 结果集不一致")
    print(f"原SQL独有的: {old_set - new_set}")
    print(f"优化后独有的: {new_set - old_set}")

print(f"加速比: {old_time / new_time:.1f}x")
conn.close()

跑完的结果:

css 复制代码
[原SQL] 返回 50 行,耗时 7.8921 秒
[优化后SQL] 返回 50 行,耗时 0.1187 秒
✅ 结果集一致
加速比: 66.5x

实话说,这个验证脚本比我自己写的好看且严谨。它甚至考虑到了结果集差异的可能------如果 refunds 表有多条记录,LEFT JOIN 可能会多出几行,AI 在注释里提醒了我这一点。

最终修复上线和长期效果

索引和 SQL 上线后,对账接口的 P99 延迟从 8 秒降到了 150 毫秒以内。持续观察了一周,数据库 CPU 使用率下降了约 15%。

更重要的是,我在这个过程中总结出了一套"AI + DBA"的协作模板,后面遇到其他慢查询,也能很快复用。

我沉淀的"慢查询优化协作模板"

现在每次遇到慢查询,我会按这个流程走:

1. 准备上下文(5 分钟)

收集三样东西喂给 AI:

  • 慢 SQL 原文
  • EXPLAIN 输出(最好是 JSON 格式 EXPLAIN FORMAT=JSON
  • 表结构(SHOW CREATE TABLE 或包含索引的部分)

2. 提问三连(让 AI 先分析)

使用固定的提示词模板:

markdown 复制代码
你是 MySQL 优化专家。以下是慢查询的诊断信息。
请严格按顺序回答:
1. 指出当前执行计划的瓶颈(哪个步骤最耗时、为什么)。
2. 给出索引优化建议(精确的 DDL),说明每个索引的设计理由。
3. 给出 SQL 改写建议(如有必要),写出优化后的 SQL。
4. 预估优化效果(为什么你觉得这样会更快)。

注意:要求 AI 按顺序回答,这样它的思考链是连贯的,不会跳步。

3. 人工验证(最关键的一步)

AI 输出后,我必须做三件事:

  • 检查索引维护成本:索引大小、加索引的 DDL 耗时、是否影响写入。
  • 检查 SQL 语义等价:LEFT JOIN 改写是否改变了行数、过滤条件是否等价。
  • 在测试环境实际跑一遍:用 AI 生成的验证脚本,对比结果和耗时。

4. 灰度上线

小流量观察,确认无误后再全量。

和其他优化方式的对比

过去我试过的优化手段:

方式 耗时 质量 风险
纯人工分析 2~4 小时 依赖个人经验,有时漏掉好方案
数据库自带 Advisor 1 秒 通用建议,不考虑业务特殊性,经常给错误索引
AI 辅助(本次) 30 分钟 类似高级 DBA 水平,但需要人把关

AI 不是替代人,而是把人从"查文档、猜方案"的循环里解放出来,把精力花在更重要的决策上。

三个重要的提醒

1. 不要把表结构和生产数据直接发给 AI

涉及数据安全,尽量只发送表结构、索引和 EXPLAIN 输出,不要发送真实数据行。必要时用脱敏数据。

2. 永远验证 AI 的输出

AI 给出的索引可能会"过度设计",比如建议一个 8 列的覆盖索引------它能加速查询,但会严重拖慢写入并占用大量内存。需要结合业务读写比例判断。

3. 学会"套娃":让 AI 校验 AI 的结果

如果你对 AI 给出的 SQL 改写不放心,可以再开一个对话,把原始 SQL 和改写后的 SQL 一起发给 AI,要求它"对比两个 SQL 是否逻辑等价,指出所有可能的语义差异"。有时候两个 AI 对话会给你交叉验证的惊喜。


参考来源

文中使用的各模型 API Key 均可从 gpt108.com 获取(该渠道提供 ChatGPT Plus、Claude Pro、Gemini Advanced、Cursor Pro 及 API 充值服务)。笔者团队生产环境已稳定运行 4 个月,仅作技术方案记录。

你在做数据库优化时用过 AI 吗?遇到过什么坑或者意外收获?欢迎评论区聊聊。

相关推荐
大熊程序猿6 小时前
ASP.NET Core 认证授权:JWT与OAuth2实战
后端·asp.net
zhangxingchao6 小时前
AI应用开发五:RAG高级技术与调优
前端·人工智能·后端
知彼解己7 小时前
Go 开发环境 安装
后端·golang
牛肉烧烤屋7 小时前
为什么大模型需要“思考模式”?
aigc·ai编程·deepseek
snakeshe10107 小时前
SpringBoot 多人协作平台实战(7):完善登录模块 —— Spring 注解体系与密码加密实践
后端
Amazing53077 小时前
Redis 7配置的三个隐藏陷阱
后端
Ting-yu7 小时前
Spring AI Alibaba零基础速成(2) ---- Ollama安装与使用
java·后端·spring·ai
qq_5470261797 小时前
SpringBoot + Redis 电商秒杀完整方案
spring boot·redis·后端
会编程的吕洞宾7 小时前
Spring_Boot_3_3_的___Transactional__
java·后端·spring