news 2026/6/11 18:21:51

我用AI生成的SQL,差点在生产库上跑了一整夜

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
我用AI生成的SQL,差点在生产库上跑了一整夜

我用AI生成的SQL,差点在生产库上跑了一整夜

目录

  • 凌晨的告警短信
  • 那段"看起来没问题"的SQL
  • EXPLAIN输出告诉我真相
  • 为什么AI不关心你的索引
  • 给AI加规则后重新生成
  • 我现在怎么用AI写SQL

凌晨的告警短信

那天凌晨两点,手机连续收到四条阿里云RDS告警:"CPU使用率超过90%","活跃连接数超过200","慢查询阈值触发"。我打开DMS连上去一看,show processlist里躺着一条SQL,Time列显示已经跑了23分钟,state是Sending data

这条SQL是我下午让AI生成的。

业务场景不复杂:运营要一个数据报表,统计最近30天各商品类目的订单转化率。我懒得手写这条跨五张表的统计查询,就把需求描述贴给了AI:"写一条MySQL查询,统计近30天每个商品类目的订单转化率,需要关联用户表、商品表、类目表、订单表和订单明细表,按转化率降序排列。"

AI很快给出了结果,我在测试环境跑了一下,数据量小的时候返回很快,看起来没问题。代码评审的时候也没人觉得有毛病——五表JOIN在报表场景太常见了。

那段"看起来没问题"的SQL

AI产出的SQL大概是这个结构(脱敏后):

sql SELECT c.category_name, c.category_id, COUNT(DISTINCT o.order_id) AS total_orders, COUNT(DISTINCT oi.order_id) AS converted_orders, ROUND(COUNT(DISTINCT oi.order_id) * 100.0 / COUNT(DISTINCT o.order_id), 2) AS conversion_rate FROM orders o LEFT JOIN users u ON o.user_id = u.user_id LEFT JOIN order_items oi ON o.order_id = oi.order_id LEFT JOIN products p ON oi.product_id = p.product_id LEFT JOIN categories c ON p.category_id = c.category_id WHERE o.create_time >= DATE_SUB(NOW(), INTERVAL 30 DAY) AND o.order_status IN (1, 2, 3, 5) AND u.user_type = 'NORMAL' GROUP BY c.category_id, c.category_name ORDER BY conversion_rate DESC;

语法没问题,格式也工整。问题出在它"不知道"的东西上。

EXPLAIN输出告诉我真相

我在生产只读库上跑了EXPLAIN,结果让人头皮发麻:

| table | type | key | rows | Extra | |-------|------|-----|------|-------| | orders | range | idx_create_time | 860000 | Using where; Using temporary; Using filesort | | users | eq_ref | PRIMARY | 1 | Using where | | order_items | ALL | NULL | 1200000 | Using where; Using join buffer | | products | eq_ref | PRIMARY | 1 | NULL | | categories | eq_ref | PRIMARY | 1 | NULL |

order_items表走了全表扫描,rows估算120万行。生产库里order_items表有接近两千万行数据。这个查询在生产上跑完估计要超过40分钟。

问题出在哪里?AI不知道这张表的实际数据分布。它不可能知道——

order_items表缺少必要的索引。order_id字段虽然在order_items表里,但这个字段没有单独的索引,它只是联合主键(order_id, item_id)的一部分。MySQL优化器在某些情况下用不到联合主键的最左前缀。

隐式类型转换。oi.product_id = p.product_id看起来正常,但order_items.product_idvarchar(32)products.product_idbigint(20)。这种类型不一致在数据量小的时候无所谓,数据量大时索引直接失效。

N+1式统计。COUNT(DISTINCT oi.order_id)在大数据量下的去重计算代价极高。AI可以写出正确的SQL语法,但它不具备"这条SQL在生产库上跑会不会出问题"的判断力。

为什么AI不关心你的索引

这里有个根本性的问题要说清楚:AI生成SQL时看到的是"表结构"和"需求描述",它看不到——

生产中每张表的实际数据量和增长率。一张表有1万行和有2000万行,最优查询策略完全不同。

索引的实际选择性与碎片率。建了索引和索引真的被用到是两码事。order_items表上明明有联合主键,但实际执行计划里MySQL就是选了全表扫描。

MySQL优化器的版本差异。同样的SQL在MySQL 5.7和8.0上执行计划可能完全不同。AI默认参考的是通用文档,不会针对你的具体版本。

给AI加规则后重新生成

发现问题后我没有自己改SQL,而是重新问AI,这次加了规则:

"重写这条SQL,注意:order_items表有2000万行,product_id是varchar类型不是数值类型,避免DISTINCT在2000万行上的性能问题,先聚合再JOIN。"

这次AI生成的SQL好得多:

sql SELECT c.category_name, c.category_id, stats.total_orders, stats.converted_orders, ROUND(stats.converted_orders * 100.0 / stats.total_orders, 2) AS conversion_rate FROM ( SELECT p.category_id, COUNT(DISTINCT o.order_id) AS total_orders, COUNT(DISTINCT CASE WHEN oi.order_id IS NOT NULL THEN o.order_id END) AS converted_orders FROM orders o JOIN users u ON o.user_id = u.user_id AND u.user_type = 'NORMAL' LEFT JOIN order_items oi FORCE INDEX(idx_order_id) ON o.order_id = oi.order_id LEFT JOIN products p ON oi.product_id = p.product_id WHERE o.create_time >= DATE_SUB(NOW(), INTERVAL 30 DAY) AND o.order_status IN (1, 2, 3, 5) GROUP BY p.category_id ) stats JOIN categories c ON stats.category_id = c.category_id ORDER BY conversion_rate DESC;

改进点很实在:

用子查询先聚合再JOIN,减少了categories表参与中间结果集。

加了FORCE INDEX(idx_order_id)提示,因为我知道order_items表上有个单独的idx_order_id索引(AI不知道,但我说了它就会用)。

把类型不一致的问题明确告诉AI后,它不会再犯同样的错。

我现在怎么用AI写SQL

这件事之后,我写SQL的流程变成了这样:

先把需求按"表名 + 实际行数 + 索引列表 + 字段类型"的格式丢给AI。比如:

orders: 860万行, 索引idx_create_time(create_time), idx_user_status(user_id, order_status) order_items: 2000万行, 索引idx_order_id(order_id), 联合主键(order_id, item_id) product_id在order_items中是varchar(32),在products中是bigint(20) 需求:统计近30天各商品类目的订单转化率

然后等AI出SQL,我自己跑EXPLAIN看执行计划。如果type列出现ALL,就要追问AI:"这个表2000万行你让它全表扫?换个写法。"

几轮下来,AI产出的SQL质量明显提高。因为它从我的反馈里学到了"大表不能用ALL扫描"、"字段类型不同会导致索引失效"这些约束。

这不是AI变聪明了,是我给的上下文变丰富了。

我后来在团队里做了个分享,标题就叫"AI不知道你的表有多大"。分享完后一个同事说:"这不就是欺负AI没见过生产数据嘛。"我说对,所以你得替它见。

写SQL这件事上,AI能帮你省掉80%的敲键盘时间,但剩下的20%——理解数据规模、看懂执行计划、避开隐式类型转换——还得你自己来。这20%才是区分"能用"和"出事故"的关键。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 18:19:51

HTML转Figma完整指南:3步实现网页到设计的快速转换

HTML转Figma完整指南:3步实现网页到设计的快速转换 【免费下载链接】figma-html Convert any website to editable Figma designs 项目地址: https://gitcode.com/gh_mirrors/fi/figma-html 在现代Web开发与设计流程中,设计师与开发者之间常常存在…

作者头像 李华
网站建设 2026/6/11 18:11:36

深入解析OL2381分数分频PLL:原理、配置与FSK/ASK调制实战

1. 项目概述与PLL核心价值 在Sub-1 GHz频段的无线通信系统里,无论是智能家居的传感器节点,还是工业物联网的遥控器,其心脏都是一个稳定且精准的射频信号源。这个信号源负责两件核心大事:一是为接收机(RX)提…

作者头像 李华
网站建设 2026/6/11 18:10:46

Paperxie 论文降重降 AIGC 功能详解,适配各大高校检测系统

paperxie-免费查重复率aigc检测/开题报告/毕业论文/智能排版/文献综述/课程论文降重复率 - PaperXie智能写作PaperXie免费论文查重检测-首款免费论文检测软件,为毕业生提供专业的论文重复率检测、论文降重、Aigc检测、智能排版 、论文写作等一站式服务。https://www.paperxie.c…

作者头像 李华
网站建设 2026/6/11 18:09:56

Python 多线程接口健康检查:生产级实践指南

为什么需要多线程做健康检查? 假设你有 50 个服务节点要检查。 单线程: 50 个 每个 0.8 秒 40 秒多线程(10 并发): 50 个 10 线程 ≈ 4 秒差了 10 倍。 健康检查本身就是 IO 密集型,多线程几乎零成本提速…

作者头像 李华
网站建设 2026/6/11 18:09:00

H5应用如何突破浏览器限制,精准识别移动设备型号与唯一标识

1. 为什么H5应用难以获取设备真实信息? 每次在H5项目中遇到需要获取设备型号的需求,我都会想起第一次踩坑的经历。当时产品经理要求做一个"根据手机型号推荐配件"的功能,我信心满满地开始写代码,结果发现浏览器能提供的…

作者头像 李华