mysql索引和锁之间有什么关系_mysql并发原理说明

来源:这里教程网 时间:2026-02-28 20:41:55 作者:

索引没建好,行锁会自动升级成表锁

MySQL 的行锁不是“对行加锁”,而是“对索引项加锁”。InnoDB 只能通过索引定位数据,如果

WHERE
条件字段没有索引(或索引失效),优化器无法快速定位记录,就会退化为全表扫描——此时为了保证一致性,InnoDB 会为**所有扫描过的记录**加锁;而最坏情况下,它可能对**聚簇索引的每条记录都加 X 锁**,效果等同于锁表。

常见错误现象:
UPDATE user SET status=1 WHERE phone='138xxx'
执行极慢、阻塞其他事务,
SHOW ENGINE INNODB STATUS
显示大量
lock_mode X locks rec but not gap
且涉及成千上万行
实操建议:执行前先用
EXPLAIN
确认是否走了索引;对高频查询/更新的字段(如
phone
order_no
)务必建立单独索引或作为联合索引的最左前缀
特别注意:
LIKE '%abc'
OR
混合条件、函数包裹字段(如
WHERE DATE(create_time) = '2025-01-01'
)都会导致索引失效,触发隐式锁表

为什么加了索引还锁不住“该锁的行”?临键锁(Next-Key Lock)才是默认行为

InnoDB 默认使用

REPEATABLE READ
隔离级别,其行锁实际是
Next-Key Lock
(临键锁):即「记录锁 + 间隙锁」的组合。它不仅锁住匹配的索引记录,还会锁住该记录与前一条记录之间的「间隙」,目的是防止幻读。

使用场景:执行
SELECT * FROM order WHERE amount > 100 FOR UPDATE
时,即使表中当前只有
amount=150
amount=200
两条记录,InnoDB 也会锁定 (100, 150)、(150, 200)、(200, +∞) 这三个间隙,阻止其他事务插入
amount
在这些范围内的新订单
参数差异:若改用
READ COMMITTED
隔离级别,InnoDB 会退化为只加「记录锁」(Record Lock),不加间隙锁——幻读风险上升,但并发写入能力提升
容易踩的坑:业务以为
SELECT ... FOR UPDATE
只锁查到的几行,结果发现插入操作被莫名阻塞,根源就是临键锁锁住了“不该插的位置”

共享锁(S锁)和排他锁(X锁)的兼容性决定并发读写能否并行

锁的本质是资源访问的互斥协议。InnoDB 中,

S 锁
SELECT ... LOCK IN SHARE MODE
)允许多个事务同时持有,但会阻塞任何
X 锁
;而
X 锁
SELECT ... FOR UPDATE
UPDATE/DELETE
)一旦加上,其他事务既不能加
S 锁
也不能加
X 锁

常见错误现象:两个事务先后执行
SELECT ... FOR UPDATE
查询同一行,第二个事务卡住直到第一个提交——这是正常行为;但如果两个事务都只执行
SELECT ... LOCK IN SHARE MODE
,则可以并发成功
实操建议:读多写少场景下,优先用
LOCK IN SHARE MODE
替代
FOR UPDATE
,减少写冲突;更新前务必确认是否真需要独占锁,避免过度加锁拖慢整体吞吐
性能影响:S 锁之间不互斥,开销远小于 X 锁;但大量 S 锁仍会占用锁结构内存,极端情况下触发
Lock wait timeout exceeded

MVCC 不是“不用锁”,而是让读操作尽量避开锁

MVCC(多版本并发控制)让普通

SELECT
(不带
FOR UPDATE
LOCK IN SHARE MODE
)无需加锁,直接读取事务开启时刻的快照版本。但这只对「一致性非锁定读」有效;一旦涉及修改(
UPDATE
DELETE
)或显式加锁读,InnoDB 仍必须获取 X 锁或 S 锁来保证隔离性。

关键区别:
SELECT * FROM user WHERE id=123
→ 走 MVCC,无锁;
SELECT * FROM user WHERE id=123 FOR UPDATE
→ 必须加 X 锁,哪怕数据没变
容易踩的坑:误以为“开了 MVCC 就不会锁表”,结果在高并发更新场景下,因索引缺失或临键锁范围过大,导致大量事务排队等待,监控看到
innodb_row_lock_waits
持续上涨
验证方式:可通过
SELECT * FROM information_schema.INNODB_TRX
查看当前活跃事务及其锁等待状态,结合
INNODB_LOCKS
INNODB_LOCK_WAITS
定位具体阻塞链

索引和锁的关系不是“有索引就安全”,而是“索引质量直接决定锁的粒度和范围”。一个没走索引的

UPDATE
可能锁全表,而一个设计不当的联合索引(比如把低区分度字段放最左)会让临键锁覆盖大片无关数据——这些细节,在压测和慢日志分析里才真正暴露出来。

相关推荐