最近帮朋友调一个慢得像蜗牛的SQL查询,他兴冲冲说:‘我开了SQL Server的自动建议,它让我加了个非聚集索引,结果跑得更卡了!’
优化器不是算命先生
数据库的索引优化器(比如SQL Server的Database Engine Tuning Advisor、MySQL的EXPLAIN ANALYZE、PostgreSQL的pg_stat_statements+pg_hint_plan)确实会根据当前执行计划和统计信息给出“加什么索引”的提示。但它不看未来——不看你下周要加的WHERE条件,不记得上个月那个只跑一次的报表查询,更不知道你表里95%的数据是2024年新增的,而统计信息还停在去年10月。
常见不准的3种情况
① 统计信息过期
比如一张订单表,每天新增5万条,但DBA半年没更新统计信息。优化器以为“status = 'shipped'”只占0.1%,实际已占82%。它建议你给status建索引?加完发现全表扫描变索引扫描再回表,反而更慢。
② 参数嗅探“骗”了它
写了个带@city参数的存储过程:
CREATE PROCEDURE GetUserByCity @city NVARCHAR(20)
AS
SELECT * FROM users WHERE city = @city;优化器按第一次传入的‘北京’生成计划,缓存下来。后来传‘拉萨’(只有3条数据),却仍走为‘北京’定制的索引扫描+回表——而这时候直接走聚集索引可能更快。③ 它只盯单条语句,不管整体负载
某条慢查询被建议加一个覆盖索引:(order_date, status, amount) INCLUDE (user_id)。单独跑确实快了3倍。但这个索引让INSERT慢了15%,又让另一张高频更新的报表表并发下降。优化器可不会提醒你:“兄弟,你这张表每秒写200次,加这个索引后锁等待翻倍。”
怎么用才靠谱?
把它的建议当“草稿”,不是“圣旨”。拿到提示后,三步验证:
• 查统计信息更新时间:SQL Server用DBCC SHOW_STATISTICS('orders', 'IX_status');;MySQL看SHOW INDEX FROM orders;里的Cardinality是否合理;
• 用真实数据量压测:别只拿10条测试数据跑,导100万行进测试库,对比加索引前后IO次数、逻辑读、执行时间;
• 看执行计划变化细节:重点盯“实际行数 vs 预估行数”是否偏差超5倍,“是否引入Key Lookup”“是否触发Sort/Spill to TempDB”。
记住:最懂你业务的是你自己。优化器只是个经验丰富的老司机,给你指了条路,但路上有没有修路、堵车、限高,还得你亲自踩一脚油门试试。