场景
Mysql数据库,InnoDB引擎,事务级别可重复读。
表结构如下:
用比较ver实现乐观锁功能。代码大致逻辑是:
1、开启一个事务
2、查询test_table里id=1的数据,得到ver=2
3、处理业务逻辑
4、更新id=1这条数据的name为张四,ver=3,条件是ver=2,,id=1
5、如果更新失败,重复第2、3、4步。
问题
只要出现更新失败,就会一直2、3、4步不成功。
原因
执行第2步之后,id=1的数据被另一个事务将ver更新为3了。因为事务级别可重复读下,select采取的是快照读,update是当前读,所以select 查到的数据一直是当前事务开启时的快照(即ver=2,,但实际ver=3),update时,是当前读,条件ver=2当然会不成功。
示例
先开启一个事务A,执行:begin;
select name,ver from test_table where id =1;此时结果如下所示。
这时,执行另一个事务B:
begin;
update test_table set ver = 3 where id = 1;
commit;
再执行第一个事务中的update语句,显示实际更新行数0:
此时,我们另打开一个窗口,查id=1的这条数据,结果如下:
ver=3,。这时,事务A还没有提交,我们在事务A中,再执行一次select语句,结果如下:
得到的仍然是ver=2。
解决办法
将查询从事务中提出来或者将重试逻辑从事务中独立出来(即A调B,在A中重试,B中开启事务)。