一、并发业务场景
库存业务,
stock(sid, num)
,其中:
如上图所示,两个
并发的查询库存操作
,同时从数据库都得到了库存是
5
。
接下来用户发生了
并发的库存扣减动作
:
如上图所示:
这两个设置库存的接口并发执行,
库存会先变成
2
,再变成
3
,导致数据不一致
(实际卖出了
5
件商品,但库存只扣减了
2
,最后一次设置库存会覆盖和掩盖前一次并发操作)
二、不一致原因分析
出现数据不一致的根本原因,是设置操作发生的时候,没有检查库存与查询出来的库存有没有变化,理论上:
实际执行的时候:
三、
CAS
优化
大家常说的“
Compare And Set
”(
CAS
),是一种常见的降低读写锁冲突,保证数据一致性的乐观锁机制。
针对上述库存扣减的例子,
CAS
升级很容易,将库存设置接口执行的
SQL
:
update stock set num=$num_new where sid=$sid
升级为:
update stock set num=$num_new where sid=$sid
and num=$num_old
即可。
四、什么是
ABA
问题
CAS
乐观锁机制确实能够提升吞吐,并保证一致性,但在极端情况下可能会出现
ABA
问题。
什么是
ABA
问题?
考虑如下操作:
上述并发环境下,
并发
1
在修改数据时,虽然还是
A
,但已经不是初始条件的
A
了
,中间发生了
A
变
B
,
B
又变
A
的变化,
此
A
已经非彼
A
,数据却成功修改,可能导致错误,这就是
CAS
引发的所谓的
ABA
问题。
库存操作,出现
ABA
问题并不会对业务产生影响。
再看一个堆栈操作的例子:
并发
1
(上):读取栈顶的元素为“
A1
”
并发
2
:进行了
2
次出栈
并发
3
:又进行了
1
次出栈
并发
1
(下):实施
CAS
乐观锁,
发现栈顶还是“
A1
”,于是修改为
A2
此时会出现
系统错误
,
因为此“
A1
”非彼“
A1
”
五、
ABA
问题的优化
ABA
问题导致的原因,是
CAS
过程中只
简单进行了“值”的校验
,再有些情况下,“值”相同不会引入错误的业务逻辑(例如库存),有些情况下,“值”虽然相同,却已经不是原来的数据了。
优化方向
:
CAS
不能只比对“值”,
还必须确保的是原来的数据
,才能修改成功。
常见实践
:
“版本号”的比对
,
一个数据
一个版本
,版本变化,即使值相同,也不应该修改成功。
库存的并发读写例子,引入版本号的具体实践如下:
(
1
)库存表由
stock(sid, num)
升级为