治理遗留系统中的“生肉 SQL”:一次用多模型协作优化慢查询的实战复盘
接手历史包袱沉重的电商遗留系统最怕的不是报错停机而是系统“半死不活”的慢。上周我们组的订单列表页开始频繁触发慢查询告警数据库 CPU 飙升。经过日志定位罪魁祸首是一段长达 180 行、嵌套了 5 层子查询、还带有各种 MyBatis 动态if标签的“生肉 SQL”。面对这种没有任何注释、历经多代外包人员“叠罗汉”般修改的复杂查询传统的人肉格式化、逐行解读 Explain 执行计划不仅极其消耗脑力而且很容易在错综复杂的 JOIN 关系中看漏条件。为了快速找到症结所在我决定把这块硬骨头交给 AI 来拆解。因为复杂的 SQL 解析和索引推演很容易出现“幻觉”比如 AI 让你建一个由于数据倾斜根本无效的索引我需要对比不同模型在底层数据库逻辑上的推断。为了省去来回切页面的麻烦我这次排查用的是一个能在同一界面切换 ChatGPT、Claude、DeepSeek、Gemini 等大模型的聚合环境方便把同一套脱敏后的表结构、慢 SQL 和执行计划交给不同模型复跑从而在它们的共识和分歧中找到最优解。这篇文章就来复盘一下我是如何利用大模型的工作流将一段近乎无解的遗留长 SQL一步步拆解、优化并安全落地的。一、降噪与结构化让 AI 理清 JOIN 迷宫几百行的 SQL 如果直接丢给眼睛看满屏幕的LEFT JOIN和UNION会让人瞬间失去焦点。很多时候这个长 SQL 里面有一半的联表在当前特定业务场景下其实是毫无意义的。第一步我需要把动态 SQL 转化为可读的结构化逻辑。在处理长文本逻辑梳理上Claude 往往能体现出极强的连贯性而 DeepSeek 在代码/SQL语义的理解上也非常敏锐。输入 Prompt 示例你现在是一个资深的 MySQL DBA 和业务架构师。 以下是一段脱敏后的历史电商订单查询 SQL已去除真实表名用 A、B、C 代替。 请帮我完成以下任务 1. 不要逐行解释语法直接输出该 SQL 的核心业务意图是什么 2. 梳理出该 SQL 所有的过滤条件WHERE和关联关系JOIN并以 Markdown 表格形式列出表别名 | 关联字段 | 过滤条件 | 涉及的业务状态。 3. 帮我指出在这段逻辑中是否存在明显的“笛卡尔积”风险或者毫无必要的冗余 JOIN [附带脱敏后的 SQL]实测对比发现在这个环节ChatGPT 给出了一个非常中规中矩的语法树解析但 Claude Opus 和 DeepSeek 明显看出了业务端的不合理之处。DeepSeek 直接指出“表 C发票表的LEFT JOIN虽然存在但在最外层的WHERE条件中并没有使用表 C 的任何字段进行过滤且SELECT列中也没有用到如果业务上不需要发票数据这个 JOIN 完全是性能累赘。”仅凭这一句话就帮我剔除了 SQL 中 30 多行的无用逻辑。二、索引推演结合 DDL 与 Explain 的深度诊断SQL 瘦身之后还需要解决真正的慢查询问题。很多开发者用 AI 优化 SQL 时只给一条 SQL 语句这就好比让医生不看病历直接开药。为了得到准确的索引优化建议必须将脱敏后的 DDL表结构定义和Explain 执行计划结果一并提供给模型。调优诊断 Prompt 示例以下是上述优化后 SQL 的 Explain 执行计划结果以及涉及的核心表 A 和表 B 的脱敏 DDL包含现有索引。 表 A 当前数据量约 2000 万表 B 约 500 万。 请结合 DDL 和 Explain 结果进行排查 1. 指出 Explain 中哪一步触发了 filesort 或 Using temporary或者全表扫描typeALL 2. 为什么现有的 idx_user_status 索引没有被命中请分析隐式转换或最左前缀失效的可能性 3. 给出 2 种优化方案 - 方案一不改动 SQL仅通过新增/修改联合索引解决 - 方案二重写 SQL例如将 IN 子查询改为 JOIN或使用延迟关联。 请给出具体理由不要编造不存在的字段。分析与交叉验证拿到这个结果后我重点对比了不同模型的“解题思路”。Gemini 发现了一个潜在的字符集隐式转换问题表 A 的order_no是utf8mb4而关联的一张临时表是utf8导致索引失效。同时ChatGPT 和 DeepSeek 都建议采用“延迟关联Deferred Join”的方式先通过聚簇索引覆盖查询分页查出主键 ID再用这些 ID 回表查询其他字段。这在处理千万级大表的分页慢查询时是极其经典的优化手段。在多模型的交叉验证下我们确认了“索引隐式失效 深度分页”是造成这次慢查询的核心根因。三、代码重构拆解大 SQL 的技术债基于 AI 给出的“延迟关联”方案我们需要重写 MyBatis 的 XML。过去大家习惯把所有逻辑塞在一条 SQL 里让数据库扛现在的趋势是将复杂逻辑上浮到 Java 内存中处理。我要求 AI 将原来的一条大 SQL拆分为两步走先查主键再批量查详情。代码拆分 Prompt 示例基于之前的分析请帮我将这段复杂的慢 SQL 重构为 Java 内存中的两步查询逻辑。 技术栈为 Spring Boot MyBatis Plus。 1. 第一步编写仅查询订单 ID 列表的 SQL 及对应的 Mapper 方法 2. 第二步编写根据 ID 列表批量查询详情的 SQL 3. 第三步在 Service 层编写使用 Java 8 Stream API 组装这两批数据的代码 4. 考虑到 ID 列表可能很大请在 Service 层代码中加入按 500 个 ID 为一批次进行分批查询的防护逻辑。AI 生成的代码结构非常清晰尤其是分批查询的防御性编程如Lists.partition的使用直接省去了我在重构时可能遗漏的内存溢出风险。拿到代码后只需将变量名恢复为真实业务名称补充对应的单元测试即可。四、安全脱敏与落地的红线利用大模型治理数据库技术债虽然高效但直接关乎线上稳定和数据安全有几条底线绝对不能碰绝对脱敏原则发给外部 AI 模型的所有 DDL、SQL、Explain 结果必须提前在本地通过脚本或正则替换掉真实的表名如order_master替换为Table_A、敏感字段名如id_card替换为Field_X。千万不要为了图省事直接复制生产环境的 Schema。AI 不懂数据倾斜模型可以基于语法和执行计划给出“最优索引”但它不知道你真实业务库里status1的数据可能占了 99%。如果按照 AI 的建议在低区分度字段上建索引反而会导致性能更差。因此AI 给出的索引方案必须人工结合实际业务数据分布进行二次评估。不能替代真实压测重构后的代码和 SQL看似优雅但在合入主干前必须在测试库构造等量级的数据进行物理 Explain 验证并做灰度压测。AI 提供的是推演逻辑而非最终结论。五、总结在这场长达一周的慢查询治理战役中大模型的角色更像是一个耐心的“结对编程导师”和“高级 DBA”。它不仅帮我在几百行无头苍蝇般的遗留 SQL 中迅速理清了关联意图还通过多模型的诊断碰撞补齐了我在底层索引失效机制上的认知盲区。对于广大开发者来说当面临祖传系统的技术债时不要上来就死磕代码。先将复杂的查询抽象出来做好数据脱敏写好带有前置背景和明确约束的 Prompt放到支持多模型的环境中跑一遍对比。把庞大的难题拆解成具体的结构化逻辑、执行推演和代码分层改造这才是使用 AI 辅助工程演进的最稳健方式。