先看执行计划,若type为ALL或Table Scan则未走索引;常见原因:字段函数、参数类型不匹配、复合索引最左前缀未命中。

存储过程里 WHERE 条件没走索引,怎么办
直接看执行计划,EXPLAIN 或 SQL Server 的 SET STATISTICS XML ON 是唯一靠谱入口。别猜,先跑一遍——如果 type 是 ALL(MySQL)或 Table Scan(SQL Server),说明确实没用上索引。
常见原因有三个:
-
WHERE中对字段用了函数,比如WHERE YEAR(create_time) = 2023→ 改成WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31' - 参数类型不匹配,例如存储过程中声明
@id VARCHAR(10),但表里id是INT,隐式转换会让索引失效 - 复合索引顺序错了,比如建了
(status, created_at),但查询只写了WHERE created_at > '2023-01-01',最左前缀没命中,索引被跳过
存储过程里 JOIN 性能差,索引怎么配
JOIN 是存储过程里最常出问题的环节,尤其多表关联时。关键不是“有没有索引”,而是“JOIN 字段上有没有索引”,且两边字段类型、长度、排序规则必须一致。
实操建议:
- 确保
ON子句中每张表的关联列都有单列索引,或联合索引覆盖整个ON条件 - 大表放左边还是右边?不重要;重要的是小结果集先过滤。比如
WHERE u.status = 1后只剩 100 行,那就先查users再JOIN order_log,别反过来 - 避免在 JOIN 后再加复杂
WHERE,比如JOIN ... WHERE o.amount > 1000 AND u.name LIKE '%john%'—— 这种模糊匹配大概率触发全表扫描,不如提前把u.name拆出去做临时表或 CTE 预处理
存储过程频繁重编译,索引还能起作用吗
不能。执行计划缓存失效时,索引再好也白搭。SQL Server 里典型诱因是参数嗅探(Parameter Sniffing):第一次用 @status = 1 生成的计划,缓存后被 @status = 0 复用,结果走错索引路径。
快速验证方式:SELECT * FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) 查最近执行的语句,看 execution_count 和 plan_generation_num 是否频繁变化。
临时缓解方法(不改逻辑前提下):
- 加
OPTION (RECOMPILE)强制每次重编译(适合参数差异极大、执行频次低的场景) - 用
OPTIMIZE FOR (@status = 1)固定一个典型值生成计划 - 拆分逻辑:把高频稳定参数和低频变动参数分开进不同存储过程,避免混用
索引建多了,存储过程反而变慢了
写操作变慢只是表象,真正卡点常在锁和日志压力。存储过程里如果有批量 UPDATE 或 INSERT,每个索引都要同步更新,事务日志写放大,还可能引发页分裂和锁升级。
判断是否过度索引,看这两点:
- 该表
INSERT/UPDATE/DELETE频次远高于SELECT,却建了 5 个以上非聚集索引 - 执行计划里出现大量
Key Lookup(SQL Server)或Using index condition(MySQL)但伴随高rows_examined
优先保留 WHERE、JOIN、ORDER BY 中实际用到的索引,删掉仅用于 SELECT * 场景的覆盖索引——除非你明确需要避免 Key Lookup,且能接受写入代价。
真正容易被忽略的点:索引不是静态配置项。上线后业务逻辑一变,原来高效的索引可能变成负担。定期用 sys.dm_db_index_usage_stats(SQL Server)或 information_schema.statistics(MySQL)查索引使用率,零使用的就该删。


















