专栏名称: 架构师之路
架构师之路,坚持撰写接地气的架构文章
目录
相关文章推荐
架构师之路  ·  想要提升deepseek回复质量,会这一招就 ... ·  2 天前  
51好读  ›  专栏  ›  架构师之路

CAS下ABA问题及优化方案 | 架构师之路

架构师之路  · 公众号  · 架构  · 2017-06-21 21:30

正文

一、并发业务场景

库存业务, stock(sid, num) ,其中:

  • sid 为库存 id

  • num 为库存值

如上图所示,两个 并发的查询库存操作 ,同时从数据库都得到了库存是 5

接下来用户发生了 并发的库存扣减动作

如上图所示:

  • 用户 1 购买了 3 个库存,于是库存要设置为 2

  • 用户 2 购买了 2 个库存,于是库存要设置为 3

这两个设置库存的接口并发执行, 库存会先变成 2 ,再变成 3 ,导致数据不一致 (实际卖出了 5 件商品,但库存只扣减了 2 ,最后一次设置库存会覆盖和掩盖前一次并发操作)

二、不一致原因分析

出现数据不一致的根本原因,是设置操作发生的时候,没有检查库存与查询出来的库存有没有变化,理论上:

  • 仅库存为 5 的时候,用户 1 的库存设置 2 才能成功

  • 仅库存为 5 的时候,用户 2 的库存设置 3 才能成功

实际执行的时候:

  • 库存为 5 ,用户 1 set stock 2 确实应该成功

  • 库存变为 2 了,用户 2 set stock 3 应该失败掉

三、 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 ,后续计划实施 CAS 乐观锁,期望数据仍是 A 的时候,修改才能成功

  • 并发 2 :将数据修改成 B

  • 并发 3 :将数据修改回 A

  • 并发 1 (下): CAS 乐观锁,检测发现初始值还是 A ,进行数据修改

上述并发环境下, 并发 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)

升级为







请到「今天看啥」查看全文