一、“丢失更新”
一个事务的更新操作被另外一个事务的更新操作覆盖。在RR状态下,普通select的时候是会获得旧版本数据的,但是update的时候就检索到最新的数据。
解决方法:在读取的过程中设置一个排他锁,在 begin 事务里, select 语句中增加 for update 后缀,这样可以保证别的事务在此事务完成commit前无法操作记录。参考《MySQL技术内幕 InnoDB存储引擎》
二、减库存的场景
当前库存:num=200
假如多线程并发:
AB同时开启事务,A先请求到行锁,
A:
start transaction;
select num from t where num>0;先查询当前库存值(num>0)
update t set num=num-200; 库存减量
B:
start transaction;
select num from t where num>0;先查询当前库存值(num>0)
update t set num=num-200; 库存减量
----结果---
A:查询到num=200,做了库存减量成了0
B:事务启动后,查询到也是200,等 A 释放了行锁,B进行update,直接变成 -200
但是 B 查询时,时有库存的,因此才减库存,结果变成负的。
老师,对于这种场景,怎么避免减成负值?
给 select 加读锁或者写锁吗 ?这种select 加锁,对业务影响大吗?
答:
一开始Select 加锁虽然可以,但是会比较严重地影响并发数。
比较简单的做法是update语句的where 部分加一个条件: where nun >=200 .
然后在程序里判断这个update 语句的affected_rows,
如果等于1 那就是符合预期;
如果等于0,那表示库存不够减了,业务要处理一下去,比如提示“库存不足”