索引不直接提升存储过程吞吐量,而是通过加速其内部查询、减少锁等待和避免全表扫描来间接改善;吞吐量瓶颈多源于缺索引或索引未被正确使用,常见原因包括隐式类型转换、参数嗅探、函数导致索引失效及联合索引顺序不当。

索引本身不直接提升存储过程的“吞吐量”,它只加速其中的查询和连接操作;真正影响吞吐量的是存储过程内部SQL是否能高效定位数据、减少锁等待、避免全表扫描。如果你的存储过程执行慢、并发一高就卡住,大概率不是存储过程写得不好,而是底层表缺索引或索引没用对。
为什么存储过程里加了WHERE却没走索引
常见现象是:存储过程里写了 WHERE user_id = @user_id,但 EXPLAIN 显示 type = ALL 或 key = NULL。这不是语法错误,而是隐式类型转换或参数嗅探导致索引失效。
-
user_id字段是INT,但传入的参数是VARCHAR(比如从应用层拼字符串传进来),数据库会自动转成字符串比较,索引无法匹配 - SQL Server 中首次调用时用小范围参数(如
@status = 'draft')生成了窄范围执行计划,后续传入高频值(如'active')仍复用该计划,导致大量行扫描 - 在条件中对字段用了函数,例如
WHERE YEAR(created_at) = 2025,哪怕created_at有索引也完全失效 - 联合索引
(status, user_id)被写成WHERE user_id = ?—— 最左匹配没满足,整个索引作废
哪些列必须加索引才能撑住高并发调用
不是“所有WHERE列都加索引”,而是聚焦在存储过程实际执行路径上的高频过滤+连接点。重点看三类列:
- 所有
IN参数对应的查询条件列,尤其是被用于JOIN ON或WHERE的整型主键/外键,例如orders.user_id、order_items.order_id - 经常出现在
ORDER BY+LIMIT组合里的列(分页场景),如created_at DESC,建议建覆盖索引包含 SELECT 列,避免回表 - 更新类存储过程中被
UPDATE ... WHERE锁定的列,没有索引会导致全表锁或间隙锁扩大,严重拖慢并发
反例:gender 这种低基数列单独建索引几乎无用,还拖慢 INSERT;TEXT 类型列不能直接建索引,需用前缀索引或生成计算列再索引。
联合索引怎么设才让存储过程真正受益
联合索引不是把存储过程中所有WHERE列堆一起就行。顺序决定一切,尤其当存储过程有多个可选条件时。
- 把等值查询列放最左,例如存储过程支持按
tenant_id和status查询,则(tenant_id, status)比(status, tenant_id)更通用 - 范围查询列(
>、BETWEEN、LIKE 'abc%')必须放在等值列之后,且它右边的列无法走索引 —— 所以别把created_at > ?放在联合索引第一位 - 如果存储过程常查
SELECT id, name, email FROM users WHERE deleted = 0 AND status = 'active',建(deleted, status, id, name, email)覆盖索引,EXPLAIN中Extra会显示Using index,彻底避免回表
索引建完吞吐量还是上不去?检查这三点
建了索引不代表万事大吉。很多团队卡在这一步,以为“加了索引=优化完成”,结果压测 QPS 没变化。
- 确认存储过程每次执行是否真的走新索引:在 SQL Server 用
sys.dm_exec_query_stats查execution_count和total_logical_reads;MySQL 用performance_schema.events_statements_summary_by_digest对比前后rows_examined - 检查统计信息是否过期 —— 尤其是大批量导入后没更新统计信息,优化器可能误判索引效率而弃用它;SQL Server 手动跑
UPDATE STATISTICS,MySQL 5.7+ 默认自动,但大表仍建议ANALYZE TABLE - 是否存在锁竞争掩盖了索引收益?比如多个并发调用同一存储过程更新同一张表的同一行,即使索引再快,也会排队等锁 —— 此时看
sys.dm_os_waiting_tasks(SQL Server)或SHOW ENGINE INNODB STATUS(MySQL)里的lock_waits
索引是静态结构,而存储过程的执行路径受参数、数据分布、并发上下文动态影响。真正起效的索引优化,永远是从一条慢语句的 EXPLAIN 出发,而不是从“听说要加索引”开始。

















