1.共享锁 (lock in share mode)
1.1 概念
允许不同事务之前共享加锁读取,但不允许其它事务修改或者加入排他锁
如果有修改必须等待一个事务提交完成,才可以执行,容易出现死锁
1.2 例子:
共享锁事务读取
以不同的 session 来举例:
a:
start transaction;
select * from demo where id = 1 lock in share mode;
b:
start transaction;
select * from demo where id = 1 lock in share mode;
此时 a 和 b 都可以正常获取结果,那么再加入 c 排他锁读取尝试
c:
start transaction;
select * from demo where id = 1 for update;
在 c 中则无法获取数据,直到超时或其它事物 commit
共享锁事务更新
a:
update demo set name = 'xxx' where id = 1;
可以很快获取执行结果。当 b 再次执行修改 id=1 的语句时:
b:
update demo set name = 'yyy' where id = 1;
就会出现死锁或者锁超时,错误如下:
Deadlock found when trying to get lock; try restarting transaction
或者:
Lock wait timeout exceeded; try restarting transaction
必须等到 a 完成 commit 动作后,b 才会正常执行,如果此时多个 session 并发执行,可想而知出现死锁的几率将会大增。
c 则更不可能
1.3应用场景
拿mysql官方文档的例子来说,一个表是child表,一个是parent表,假设child表的某一列child_id映射到parent表的c_child_id列,那么从业务角度讲,此时我直接insert一条child_id=100记录到child表是存在风险的,因为刚insert的时候可能在parent表里删除了这条c_child_id=100的记录,那么业务数据就存在不一致的风险。
正确的方法是再插入时执行select * from parent where c_child_id=100 lock in share mode,锁定了parent表的这条记录,然后执行insert into child(child_id) values (100)就ok了。
1.4 小结
- 允许其它事务也增加共享锁读取
- 不允许其它事物增加排他锁 (
for update
) - 当事务同时增加共享锁时候,事务的更新必须等待先执行的事务 commit 后才行,如果同时并发太大可能很容易造成死锁
共享锁,事务加多少,都能读。修改是唯一的,必须等待前一个事务 commit,才可以下个修改
2.排它锁
2.1 概念
当一个事物加入排他锁后,不允许其他事务加共享锁或者排它锁读取,更加不允许其他事务修改加锁的行。
2.2 例子
排他锁不同事务之间的读取
同样以不同的 session 来举例
a:
start transaction;
select * from demo where id = 1 for update;
b:
start transaction;
select * from demo where id = 1 for update;
当 a 执行完成后,再次执行 b,此时 b 也会卡住,无法立刻获取查询的数据。直到出现超时
Lock wait timeout exceeded; try restarting transaction
或 a commit 才会执行
那么再使用 c 加入共享锁尝试
select * from demo where id = 1 lock in share mode;
结果也是如此,和 b 一样,超时或等待 a commit
Lock wait timeout exceeded; try restarting transaction
排他锁事务之间的修改
当在 a 中执行 update 语句:
update demo set name ='xxx' where id = 1;
可以正常获取结果,接着在 b 中执行修改
update demo set name = 'yyy' where id = 1;
则会卡住直接超时或 a commit, 才会正常吐出结果
c 也很明显和 b 一样的结果,这里就不多赘述
2.3应用场景
如果是同一张表的应用场景,举个例子,电商系统中计算一种商品的剩余数量,在产生订单之前需要确认商品数量>=1,产生订单之后应该将商品数量减1。
1 select amount from product where product_name='XX';
2 update product set amount=amount-1 where product_name='XX';
显然1的做法是是有问题,因为如果1查询出amount为1,但是这时正好其他session也买了该商品并产生了订单,那么amount就变成了0,那么这时第二步再执行就有问题。使用排它锁就可以避免这种问题:
select * from demo for update
2.4 小结
- 事务之间不允许其它排他锁或共享锁读取,修改更不可能
- 一次只能有一个排他锁执行 commit 之后,其它事务才可执行
不允许其它事务增加共享或排他锁读取。修改是唯一的,必须等待前一个事务 commit,才可以下一个修改
最后
lock in share mode适用于两张表存在业务关系时的一致性要求,for update适用于操作同一张表时的一致性要求。
— —感谢浏览♥
来源:oschina
链接:https://my.oschina.net/u/4448770/blog/3164638