执行计划中的“开销”仅是优化器估算的相对值,不可跨查询或环境比较;真正需关注算子类型、行数偏差、统计信息更新及锁竞争等实际瓶颈。

怎么看执行计划里的“开销”是不是靠谱
SQL Server 的执行计划里标出的“Estimated Subtree Cost”(比如 0.0032831)只是优化器基于统计信息和模型估算出来的相对值,不是毫秒,也不是真实耗时。它只在**同一查询的不同计划分支间可比**——比如嵌套循环 vs 哈希连接哪个代价更低;跨查询、跨服务器、甚至同一条查询在不同统计信息下,这个数字都不可直接比较。
常见错误现象:SELECT * FROM orders WHERE status = 'shipped' 在执行计划里显示开销 0.001,但实际跑 8 秒;而另一条更长的 JOIN 查询开销 0.05 却秒出。这不是数据骗人,是优化器低估了 I/O 或内存压力。
- 真正该盯的是算子类型:看到
Table Scan或Index Scan而非Seek,哪怕开销低,也说明没走索引 - 留意
Actual Rows和Estimated Rows差距是否巨大(比如估 100 行,实返 50 万),这往往意味着统计信息过期,UPDATE STATISTICS比调优 SQL 更管用 - 开销数值本身受
cost threshold for parallelism配置影响——调高它会让优化器更倾向串行计划,整个开销数字就“变小”了,但不等于变快
PostgreSQL 怎么看真正的执行开销(别只信 EXPLAIN)
PostgreSQL 的 EXPLAIN 默认只输出估算,必须加 ANALYZE 才跑真实语句、收集实际耗时与行数。但注意:EXPLAIN (ANALYZE) 会真正执行,有副作用(比如触发 INSERT/UPDATE),生产环境慎用。
使用场景:开发或测试库中验证慢查询;或用 EXPLAIN (ANALYZE, BUFFERS) 看缓存命中率(shared hit 多少),这对判断是否要加大 shared_buffers 很关键。
-
Planning Time高(比如 >100ms)?说明查询结构太复杂,优化器穷举组合花太久,考虑拆分或加ENABLE_SEQSCAN=off临时引导 - 看到
Buffers: shared read=12480且hit=0?说明全盘读磁盘,不是内存不够就是缓存未预热 - 避免只看
Execution Time:它不含网络传输和客户端解析时间,真实端到端延迟往往高 2–3 倍
MySQL 的 “key_len” 和 “rows” 比 “Extra” 里的 “Using filesort” 更值得先看
MySQL 的 EXPLAIN 输出里,key_len 告诉你用了索引的几个字节,rows 是优化器预估扫描行数——这两个数比 Extra 列里那些提示更早暴露问题。比如 key_len 是 4,但字段是 VARCHAR(255),说明只用了前缀索引的前 4 字节,很可能匹配不准;rows 显示 128000 却没 Using index,基本等于要扫 12 万行再回表。
容易踩的坑:当 type 是 range 但 rows 过大,别急着加索引——先查 SHOW INDEX FROM table,确认现有索引是否覆盖了 WHERE + ORDER BY 字段;否则加了也是白加。
-
key_len为NULL?代表根本没走索引,检查 WHERE 条件是否对字段用了函数(如WHERE YEAR(created_at) = 2023) -
rows估得离谱(比如实际 10 行,显示 98231)?运行ANALYZE TABLE table_name更新统计信息 -
Using filesort不一定慢,但如果同时出现Using temporary+rows> 1000,说明排序无法走索引,ORDER BY 字段必须进联合索引
为什么“开销低”的查询反而卡住整个数据库
执行计划开销只反映单次查询的 CPU+I/O 模型估算,完全不体现锁竞争、内存争用、或长事务阻塞。一个开销 0.0002 的 UPDATE 如果锁住主键 5 秒,能让后面所有依赖该行的查询排队等死——而它的执行计划看起来干净又轻量。
真正危险的不是高开销,而是“高频+低开销+写操作”。比如每秒 200 次的 UPDATE counters SET value = value + 1 WHERE id = ?,执行计划永远显示 const 类型、rows=1,但累积的行锁和日志写入能把磁盘 IO 打满。
- 查
sys.dm_exec_requests(SQL Server)或information_schema.INNODB_TRX(MySQL)找wait_type/TRX_WAITING,比盯着开销数字有用十倍 - 用
pg_stat_statements(PostgreSQL)看total_time / calls平均耗时,再对比min_time和max_time——如果差距极大,说明受外部因素干扰严重 - 不要相信“这个 SQL 开销才 0.003,不可能有问题”——它可能正安静地 hold 住一行记录,让下游 5 个服务一起超时










