验证索引是否生效的唯一可靠方式是执行explain:type为all或index、key为null、rows接近总行数均表明未走有效索引;联合索引须遵循等值在前范围在后原则;锁行数取决于实际扫描行数而非匹配行数。

EXPLAIN 看执行计划,别猜索引有没有生效
没走索引的 UPDATE 或 SELECT ... FOR UPDATE 不会报错,但实际会全表扫描并逐行加锁,等效锁全表。验证唯一可靠的方式是跑 EXPLAIN:
-
type字段为ALL或index→ 全表或全索引扫描,肯定没走有效索引 -
key字段为NULL→ 没命中任何索引(哪怕字段上有索引,也可能因隐式转换失效) -
rows接近表总行数(比如表有 80 万行,rows显示 79 万)→ 实际扫描范围过大 - 常见诱因:
WHERE created_at > '2025-01-01'但created_at无索引;WHERE user_id = '123'(user_id是BIGINT)触发隐式类型转换;WHERE UPPER(name) = 'ABC'对索引列用函数
建联合索引必须按“等值在前、范围在后”顺序
单列索引对多条件查询往往无效。比如语句是 UPDATE orders SET status = 'shipped' WHERE status = 'pending' AND created_at > '2026-01-01',建索引不能随便选顺序:
- ✅ 有效:
ALTER TABLE orders ADD INDEX idx_status_created (status, created_at)——status等值匹配后,再用created_at做范围扫描 - ❌ 无效:
ALTER TABLE orders ADD INDEX idx_created_status (created_at, status)——created_at是范围条件,status后续无法利用索引下推 - 如果还带
ORDER BY created_at DESC LIMIT 10,索引需覆盖排序方向,否则可能放弃索引改用filesort
加了索引还是锁很多行?查 INNODB_TRX 和 performance_schema.data_lock_waits
索引建对了 ≠ 锁就少了。InnoDB 的锁范围取决于“实际扫描并加锁的行数”,不是 WHERE 匹配的行数:
- 非唯一索引的等值查询(如
status = 'pending')会锁住所有匹配值的记录 + 对应间隙(Gap Lock),若该值占比 95%,照样锁几十万行 - 用
SELECT * FROM information_schema.INNODB_TRX查trx_rows_locked字段,对比加索引前后是否显著下降 - 用
SELECT * FROM performance_schema.data_lock_waits看谁在阻塞谁,BLOCKING_TRX_ID和BLOCKED_TRX_ID能直接定位冲突事务 - READ COMMITTED 隔离级别可禁用间隙锁,减少锁范围,但要确认业务能否容忍幻读
分批更新比硬扛更实用
当索引已建、但匹配行仍极多(比如日志表按时间范围批量更新),强行一次更新容易卡死其他事务。不如主动拆解:
- 先用索引查 ID 列表:
SELECT id FROM logs WHERE created_at - 再分批更新:
UPDATE logs SET archived = 1 WHERE id IN (1,2,3,...,500) - 每次提交事务,释放锁;循环直到处理完。虽然总耗时略长,但锁持有时间短、并发干扰小
- 注意:
IN列表不宜过长(一般不超过 1000 项),避免 SQL 解析和网络传输开销上升
真正难的不是建索引,而是意识到“锁了多少行”和“WHERE 匹配了多少行”根本不是一回事。只要没查 INNODB_TRX.trx_rows_locked,就不算真正看到锁行为。











