生成 SQL 后如何检测全表扫描等慢 SQL?
未标记
考查重点:执行计划与真实扫描成本。解析SQL并看EXPLAIN的扫描类型、估算行数与索引;全表扫描不必然慢,应结合表规模、过滤率、超时和实际执行代价判断
本题目录
参考答案
核心回答
先解析并限制候选 SQL,再在受控连接上查看 EXPLAIN 的执行计划,结合表规模、预计扫描量、连接方式和资源预算决定是否执行。全表扫描是风险信号,不是慢 SQL 的充分条件;计划估算也不是实际耗时。
普通 EXPLAIN 主要展示优化器计划;EXPLAIN ANALYZE 会真正执行查询,不能作为“执行前的无成本安全检查”直接运行未经约束的模型 SQL。
计划里看什么
以 MySQL 为例,type=ALL 表示全表扫描,key 表示实际选择的索引,不能把 possible_keys 当成已经命中索引。继续看每一步估算的 rows、filtered、连接次序,以及排序、临时表等额外操作;多表查询还要检查连接条件和中间结果是否可能膨胀。也可使用结构化计划,方便程序提取风险信号。
没有索引不一定值得拦截:很小的维表或需要读取大部分数据的分析查询,扫描可能合理。反过来,命中了索引也不保证快,大量回表、多次嵌套循环或较大的中间结果仍可能昂贵。Using filesort 说明需要额外排序,不等于一定落磁盘。
估算依赖统计信息和数据分布,不能把估算行数当作真实扫描行数,也不能给所有业务套用一个固定阈值。
怎样接入问数链路
先用语法树检查语句类型、对象权限和明显危险结构,再查看计划。结合数据源等级、表规模、分区或时间条件、估算代价设置预算;高风险查询可以要求缩小时间范围,或改走预聚合数据。改写后重新检查业务口径和计划,不能为了走索引擅自删除用户需要的数据。
执行时仍保留只读权限、超时、并发与结果大小限制,并支持取消查询。LIMIT 主要约束返回量,不能保证聚合、排序或连接只处理少量底层数据。
实际慢查询应在获准的测试环境或受控时段结合真实耗时、扫描量和计划分析,必要时使用 EXPLAIN ANALYZE。把“语法通过”“计划预算通过”“真实运行达标”分开记录,便于识别统计失准和数据增长造成的退化。
对应教程
- 《电商问数:SQL生成与执行闭环》:阅读生成、数据库解析校验、校正与执行的分工;本文补充权限、成本和业务结果校验。
参考资料
- 国内:JavaGuide/SnailClimb:MySQL执行计划分析:核对type、key、rows、filtered与普通
EXPLAIN和ANALYZE的差异。 - 国内:JavaGuide/SnailClimb:MySQL索引详解:采用小表扫描、回表与覆盖索引的取舍,避免看到ALL就直接判慢。
- 国内:阿里云:数据库开发SQL审核:采用
SQL规范与优化审核的分工,组织执行前的独立规则检查。 - 国内:阿里云:DMS权限:参考查询操作与数据范围的权限分离,限定计划分析和实际执行的连接。
- 国外:Oracle/MySQL:EXPLAIN Statement (MySQL 8.4):根据官方文档核对
EXPLAIN输出和ANALYZE会执行查询的行为。 - 国外:PostgreSQL:Using EXPLAIN (PostgreSQL 18):根据计划文档区分估算代价、估算行数与实际测量,不把代价直接换成毫秒。
电商问数Python
核心回答
电商问数已有的是数据库预检查,还没有自动识别慢查询。生成 SQL 后,校验节点向 MySQL 提交 EXPLAIN,利用报错发现部分语法、表名或字段名问题;仓储没有读取返回的执行计划,因此“校验通过”不能说明没有全表扫描或查询足够快。
进一步实现慢查询检测,需要读取计划中的访问方式、实际选择的索引、估算行数和连接情况,再结合表规模与查询范围判断风险;执行时还要设置时间和资源限制。小维表全表扫描可能合理,大事实表扫描、关联放大或大量排序才需要结合具体场景处理,不能见到 ALL 就一律拒绝。
当前预检查怎样接入
generate_sql 先把候选查询写入 state["sql"],validate_sql 调用数仓仓储的 validate()。仓储的核心操作是:
sql = f"explain {sql}"
await self.session.execute(text(sql))
没有异常时,校验节点返回 error=None;报错时返回错误文字,条件边据此选择执行或校正。当前图在校正一次后直接进入 run_sql,没有再次预检查。数据库仓储与图编排分别说明了检查行为和真实执行顺序。
怎样增加计划判断
可以让仓储返回普通 EXPLAIN 的结构化结果,由独立步骤提取计划信息,再决定是否执行:
- 检查扫描与索引。 看
type、key、估算rows与filtered,区分可能使用的索引和实际选择的索引;不能只看possible_keys。 - 检查整个查询。 看连接条件、中间结果、排序和临时表。即使每张表都用了索引,多次匹配和大量回表也可能让整条查询变慢。
- 按业务预算处理。 结合表规模、数据分区、时间范围与允许延迟判断。风险过高时可以要求缩小范围或使用预聚合数据;改写查询后重新检查口径与计划。
例如“统计华北地区销售总额”需要关联订单事实表与地区维表。地区表很小时,ALL 可能是合理选择;订单事实表若缺少预期过滤、扫描范围很大,则应继续核查连接和索引。不能为了减少扫描,擅自给用户原本查询全部历史的问题加一个月份条件。
这些是进一步设计;当前项目还需要增加计划读取、规则判断和失败出口。执行侧的只读权限、超时与结果大小限制也应独立落实,LIMIT 只能约束返回规模,不能保证聚合或排序只处理少量数据。
怎样验证判断有效
用同一组查询保留计划、真实耗时和返回结果,比较小维表扫描、大范围事实表查询、缺少连接条件和大量排序等情况。预算阈值应基于实际数据与资源确定,不直接把估算行数当成真实扫描量。
普通 EXPLAIN 的估算依赖统计信息;需要测实际行数和耗时时,可在受控环境使用 EXPLAIN ANALYZE,但它会真实执行查询。计划判断还应与运行时超时、取消机制配合,避免把估算通过当作运行一定达标。
对应教程
- 电商问数:SQL 生成与执行闭环:回看候选查询、数据库反馈、校正与执行;计划读取和风险判断是本文进一步设计。
参考资料
- 国内:JavaGuide:MySQL 执行计划分析:采用访问方式、索引、估算行数和附加操作的解释,组织计划判断所需的信息。
- 国内:JavaGuide:MySQL 索引详解:参考小表扫描、回表成本与覆盖索引的取舍,解释不能仅凭
ALL或“用了索引”判断整条查询。 - 国内:阿里云 DMS:SQL 审核:借鉴规则审核与优化建议的独立分工,说明计划产生后还需要程序判断,而非数据库不报错就直接判定达标。
- 国内:阿里云 DMS:访问控制权限:参考查询、变更及数据范围权限的区分,说明执行授权需要独立于执行计划检查。
- 国外:MySQL 8.4:EXPLAIN Statement:核对普通计划输出、统计信息与
EXPLAIN ANALYZE的真实执行行为,限定预检查和运行测量各能说明什么。 - 国外:PostgreSQL 18:Using EXPLAIN:借鉴计划估算、实际测量和成本单位的区分,解释为何计划成本不能直接当成毫秒或运行保证。
相关面经
1 篇百度
后端开发工程师sql生成后怎么做的慢sql校验,比如全表查