select...for update 应用场景

2020-03-04  本文已影响0人  wbpailxt

业务场景:
通过数据库表来生成全局唯一的订单号。
实现细节:


该表的列

current_value是全局唯一的订单号,select出来即可作为订单号使用。
step是订单号增长步伐,当此次操作使用了current_value的值作为订单号之后,需要current_value的值以便下个订单使用。更新的方式就是为current_value的值加上step的值。
会出现的问题:
假设有两个事务,这个两个事务的操作都是先获取最新的全局唯一订单号,再更新表。

发生时间编号 事务a 事务b
1 begin
2 begin
3 select * from sequence_info where name = 'order_info'
4 select * from sequence_info where name = 'order_info'

这个时候事务a和事务b获取到current_value值是一样的,我们假设是100。

发生时间编号 事务a 事务b
5 update sequence_info set current_value = #{currentValue}+#{step} where name = 'order_info'
6 commit
7 update sequence_info set current_value = #{currentValue}+#{step} where name = 'order_info'
8 commit

到这里,不仅仅有两个订单的订单号是一样的(100,即两个事务取到的current_value的值),而且两次更新按道理说current_value的值应该是增长2个step,但目前只增长了1个step。

解决办法:
在select的时候为记录加上X型正经记录锁(X型正经记录锁这个叫法只是我的个人叫法,读者可以理解为排他锁即可)

发生时间编号 事务a 事务b
1 begin
2 begin
3 select * from sequence_info where name = 'order_info' for update

这个时候事务b再想select就需要阻塞等待事务a提交了,因为排他锁与排他锁之间不兼容。

发生时间编号 事务a 事务b
4 update sequence_info set current_value = #{currentValue}+#{step} where name = 'order_info'
5 commit
6 select * from sequence_info where name = 'order_info' for update
7 update sequence_info set current_value = #{currentValue}+#{step} where name = 'order_info'
8 commit

其实还有另外一种类似的解决方案:
通过update也是可以给记录加上X型正经记录锁的。

发生时间编号 事务a 事务b
1 begin
2 update sequence_info SET current_value = current_value+step WHERE NAME = 'order_info'
3 select * from sequence_info where name = 'order_info'
4 commit

由于update操作为记录添加的是X型正经记录锁,所以只要事务a未提交,别的事务也不能修改记录,甚至是都不能读取。

发生时间编号 事务a 事务b
5 begin
6 update sequence_info SET current_value = current_value+step WHERE NAME = 'order_info'
7 select * from sequence_info where name = 'order_info'
8 commit
上一篇 下一篇

猜你喜欢

热点阅读