跳到主内容

慢查询怎么用 AI 调优:SQL 改写、索引设计与上线前验证的完整闭环

100%
慢查询怎么用 AI 调优:SQL 改写、索引设计与上线前验证的完整闭环

结论先说:AI 能把慢查询的排查从「凭经验猜」变成「按执行计划逐项核对」,但索引该不该加、加了会不会拖慢写入,必须在真实数据量上用 EXPLAIN 验证后再上线。把 AI 当诊断助手而不是决策者,是这套流程有效的前提。

大多数人的用法是错的:把一条 SQL 丢给模型,问「怎么优化」,拿到一条 CREATE INDEX 就执行。这种做法有三个问题——模型看不到你的数据分布,看不到已有索引,也看不到这条查询在业务里的调用频率。缺了这三项,任何优化建议都只是猜测。

一、喂给 AI 之前,先备齐三样东西

这一步省不掉。少了任何一样,AI 给出的建议质量都会断崖式下降。

  1. 执行计划原文:MySQL 用 EXPLAIN ANALYZE,PostgreSQL 用 EXPLAIN (ANALYZE, BUFFERS),SQL Server 用实际执行计划。要原文,不要你的转述。
  2. 表结构与现有索引:SHOW CREATE TABLE 或 \d 表名 的完整输出,包含字符集、排序规则、联合索引的列顺序。
  3. 数据量级与倾斜度:表总行数、 WHERE 条件命中比例、关键列的唯一值数量(区分度)。

区分度这一项最常被忽略。同样是给 merchant_id 建索引,如果这个字段全表只有 10 个不同值,索引基本没用;把 create_time 放在联合索引首列才是正解。数据建模阶段确定的字段区分度,到这里就是索引设计的输入。

可直接复用的提示词骨架

以下是慢查询的上下文,请按三步回答:
1. 读执行计划,指出全表扫描、临时表、 filesort 分别出现在哪一步;
2. 定位导致索引失效的具体写法,逐条给出依据;
3. 给出候选索引方案,并说明每个方案对写入的代价。

【执行计划】
...
【表结构与现有索引】
...
【数据量级】
表 A 约 1200 万行,条件命中约 3%,create_time 区分度高,status 仅 5 个取值
【查询】
...

要求它「逐条给出依据」,是为了避免它直接跳到结论。实测下来,这一步能把建议可用率从大约一半提到八成以上。

二、让 AI 做三轮诊断,而不是一次性给结论

第一轮:读执行计划,圈出三个关键词

让模型先只做定位,不做改写。重点看三件事:type=ALL 或 Seq Scan(全表扫描)、Using temporary(临时表)、Using filesort(额外排序)。这三项通常决定了 80% 的耗时。

第二轮:定位「索引失效」的写法

这是 AI 最擅长的部分,因为模式固定。常见六类:

  • 列上套函数:WHERE YEAR(created_at) = 2024 让索引彻底失效,改成 created_at >= '2024-01-01' AND created_at < '2025-01-01'。
  • 隐式类型转换:字符串列用数字查,或数字列传了字符串,索引直接不走。
  • 前导列不匹配:联合索引 (a, b, c),只用 b 和 c 做条件时无法命中。
  • OR 连接:部分场景下会让优化器放弃索引,可改写为 UNION ALL 。
  • 不等值与 NOT IN:选择性差,优化器可能主动放弃索引。
  • 深分页:LIMIT 100000, 20 需要先扫过十万行,改成基于上一页最大 id 的游标分页。

第三轮:评估索引代价

这一步必须让模型把「代价」也写出来,不能只写收益。每加一个索引,写入时就要多维护一棵 B+ 树,磁盘占用和主从延迟都会上升。覆盖索引虽然能避免回表,但联合列越多,写放大越明显。

判断一个索引值不值,看两个数字:查询频率 × 单次收益,对比 写入频率 × 单次代价。前者明显大于后者才建。

三、实战:一次分页聚合查询的重写

GitHub 工程博客公开过一个很典型的案例,值得完整拆一遍。他们要提供仓库的 star 增长趋势,最初的做法是让调用方分页拉取 stargazers 列表再自行聚合——以 react/react 约 25.1 万 star 计算,拉完完整历史需要约 2511 次请求;更糟的是分页实现有缺陷,每个请求都会对全量 star 做一次 count 。

他们试过「先聚合再分页」:按天或周分桶,排序后再 LIMIT/OFFSET 。看起来合理,但 SQL 的实际执行顺序是——先取出全部匹配的 star,逐行分桶,排序所有桶,最后才应用 LIMIT 和 OFFSET 。也就是说,返回的数据变少了,但分组和排序的开销一点没少。按这个方式读完整历史,数据库要检查约 600 万行,因为每次请求都要把 25.1 万个 star 重新处理一遍。

最终方案只改了一处顺序:把 page 和 per_page 翻译成一个带索引的 created_at 日期范围,先用索引把行收窄到目标周,再分组。

朴素写法:全部匹配行 → 按天/周分桶 → 排序所有桶 → OFFSET/LIMIT → 返回
优化写法:索引日期范围 → 范围内分组 → 每日计数 → 返回

结果:请求数从约 2511 降到约 24,数据库检查的行数从约 600 万降到约 25.1 万——每个 star 大约只被检查一次。

这个案例给出的方法论比结论更值钱:LIMIT 只减少返回量,不减少计算量;真正的优化是让过滤条件在聚合之前生效,并且让索引承担这次过滤。把这段思路交给 AI 时,你可以直接问它「这条查询的过滤条件能不能下推到扫描阶段」。

AI 在 SQL 优化上的四个能力边界
能做的:识别固定反模式、解读执行计划、枚举候选索引、解释代价权衡。
做不了的:判断你的实际数据分布、预测线上并发下的锁竞争、感知主从延迟、替你承担上线风险。
会犯的错:凭字段名臆造索引(建议建一个根本不存在的列)、忽略已有联合索引导致重复建索引、把覆盖索引推荐成万能解。
对策:所有索引建议先查现有索引再评估;所有改写先在从库或预发环境跑 EXPLAIN 对比。

四、验证闭环:优化完不等于结束


  1. 在同等数据量上跑 EXPLAIN ANALYZE,确认扫描行数和实际耗时两个指标都下降,只降其中一个不够。

  2. 用慢日志记录优化前后的 P95 耗时,取同一时段对比,避开业务高峰期波动。

  3. 检查新索引是否让同表其他查询的执行计划变差——优化器改走新索引的情况经常发生。

  4. 观察写入侧的延迟与磁盘增长,尤其是大表的批量导入任务。

  5. 准备好回滚语句(DROP INDEX),并确认删除索引不会触发长时间的锁表。

如果你同时在做把数据库接进 AI 工具链的事情,可以先看MCP 服务器接数据库的权限与查询边界设计,那里讨论的是「让 AI 直接查库」时的安全边界,和本文的优化流程是上下游关系。

五、一份速查清单

现象先查什么常见解法
全表扫描WHERE 条件列是否有索引、是否套了函数改写条件 + 补索引
Extra 出现 filesortORDER BY 与索引列顺序是否一致调整联合索引列顺序
深分页慢OFFSET 值是否过大改成 id 游标分页
聚合查询慢过滤条件是否在聚合之前生效先按索引范围收窄再分组
加了索引没变快区分度、隐式转换、统计信息是否过期ANALYZE TABLE 后重测

AI 给的索引建议能直接上线吗
不能。至少要在预发环境用真实数据量跑一次 EXPLAIN 对比,并确认该表写入频率能承受新增索引的代价。模型看不到你的数据分布和写入压力。

为什么加了索引查询还是慢
四个常见原因:条件列上套了函数、发生了隐式类型转换、联合索引前导列没用上、统计信息过期导致优化器选错计划。先 ANALYZE 再复查执行计划。

LIMIT 能减少数据库的工作量吗
大多数情况下不能。 LIMIT 只减少返回给客户端的行数,分组、排序、聚合这些工作数据库已经完成了。要真正降成本,得让过滤条件在扫描阶段就生效。

覆盖索引是不是越多字段越好
不是。覆盖索引能避免回表,但联合列越多,写入时维护 B+ 树的代价越大,磁盘占用也越高。只把查询真正需要的列加进去。

延伸阅读:把运行时信息喂给模型的正确姿势
这篇有帮助吗?
云上的幻象
云上的幻象查看主页

七彩云博客,分享 WordPress 建站实战与 AI 工具测评,覆盖服务器运维、站长工具、软件资源与电商运营干货,专注原创实用的主题插件、网站加速与安全优化教程。

997文章4评论

相关文章

评论 (0)

欢迎你,新朋友,感谢参与互动!文明发言,理性交流 · 首次评论将在审核后展示