mysql排他锁和共享锁有什么区别_mysql锁模式解析

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

排他锁(X锁)和共享锁(S锁)最本质的区别是什么

区别不在“能不能读”,而在“能不能让别人同时写或读写混合”——

SELECT ... FOR UPDATE
加的是排他锁,它会彻底拦住其他事务对同一行加任何锁(包括
SELECT ... LOCK IN SHARE MODE
);而
SELECT ... LOCK IN SHARE MODE
加的是共享锁,只拦写、不拦读,多个事务可以同时持有它。

共享锁(S锁):事务A加了S锁,事务B还能立刻加S锁,但加
FOR UPDATE
会被阻塞
排他锁(X锁):事务A加了X锁,事务B再想加S锁或X锁,全都会被阻塞,直到A提交或回滚 普通
SELECT
不加任何锁(快照读),除非显式加锁或开启串行化隔离级别
INSERT
/
UPDATE
/
DELETE
默认隐式加X锁,无需手动写
FOR UPDATE

什么时候该用
FOR UPDATE
,什么时候用
LOCK IN SHARE MODE

核心看业务是否要“读完就改”——如果只是校验数据后准备更新,必须用

FOR UPDATE
;如果只是读取做展示或只读校验(比如查余额够不够、但不扣款),用
LOCK IN SHARE MODE
更轻量。

典型场景:
SELECT balance FROM account WHERE id = 123 FOR UPDATE;
→ 防止并发扣款时超扣
反例:
SELECT name, status FROM user WHERE id = 456 LOCK IN SHARE MODE;
→ 没后续更新,纯属多此一举,去掉锁更高效
注意:如果查询条件没走索引(例如
WHERE name = 'xxx'
name
无索引),
FOR UPDATE
会升级为表锁,严重拖慢其他操作

为什么有时加了锁还出现脏读/幻读?不是说锁能解决一切吗

锁只管“当前读”,不管“快照读”。InnoDB 默认的

REPEATABLE READ
隔离级别下,普通
SELECT
走的是 MVCC 快照,根本看不到你刚加的锁保护的数据版本;只有显式加锁的语句(
FOR UPDATE
/
LOCK IN SHARE MODE
)才触发当前读,看到最新已提交数据。

现象举例:事务A执行
SELECT ... FOR UPDATE
锁住某行并修改,但还没提交;事务B执行普通
SELECT
,仍能看到旧值(快照);只有B也执行
SELECT ... FOR UPDATE
才会被阻塞或看到新值
幻读问题:即使对现有行加了X锁,也无法阻止其他事务插入满足相同
WHERE
条件的新行(除非配合间隙锁或使用
SELECT ... FOR UPDATE
+ 唯一索引/主键覆盖)
真正起作用的,是锁 + 隔离级别 + 索引三者协同;单靠一个
FOR UPDATE
无法包打天下

意向锁(IS/IX)不是用户直接操作的,但它怎么影响你的加锁行为

意向锁是InnoDB自动加的表级“占位符”,你没感知,但它决定了你能否顺利拿到行锁——比如事务A正在对某行加X锁,它会先在表上加

IX
锁;此时事务B想对该表加表级
LOCK TABLES ... WRITE
,就会被立刻拒绝,因为IX和表写锁冲突。

你不需要写
INTENTION SHARE
这种语句,MySQL会在你执行
SELECT ... LOCK IN SHARE MODE
前自动加
IS
如果你看到
SHOW ENGINE INNODB STATUS
里有大量
waiting for table metadata lock
,往往是因为某个长事务持有了IX锁(比如一个未提交的
UPDATE
),而另一个DDL(如
ALTER TABLE
)在等表级独占锁
排查时别只盯着行锁,
INFORMATION_SCHEMA.INNODB_TRX
INNODB_LOCK_WAITS
才是定位锁等待链的关键

锁不是越重越好,也不是加了就万事大吉。真正难的,是在读写混合、索引缺失、事务边界模糊的场景下,判断哪一行实际被锁、谁在等谁、为什么没按预期阻塞——这些细节,藏在执行计划、锁类型输出和事务状态里,而不是语法本身。

相关推荐