当前位置: 首页 > 科技观察

CAS下ABA问题及优化方案

时间:2023-03-13 03:16:22 科技观察

1.并发业务场景下的库存业务,stock(sid,num),其中:sid为库存id,num为库存值如上图所示,两个并发查询库存操作,同时从数据库中得到的库存都是5。接下来,用户有一个并发的库存扣减动作:如上图:用户1购买了3只股票,所以库存应该设置为2用户2购买了2只股票,所以stock应该设置为3。这两个设置stock的接口是并发执行的,库存会先变成2,再变成3,导致数据不一致(实际卖了5件,只是扣了库存2,最后一次设置库存会覆盖并覆盖之前的并发操作)2.不一致分析数据不一致的根本原因是发生设置操作时,库存没有变化,而查询库存。理论上:只有当库存为5时,用户1的库存设置2才可以仅当库存为5时,用户2的库存设置3才能成功执行:库存为5,用户1的设置库存2确实应该成功将库存改为2、用户2的setstock3应该失败掉3、CAS优化大家常说的“CompareAndSet”(CAS)是一种常见的乐观锁机制,减少读写锁冲突,保证数据一致性。上面的库存扣减例子,CAS升级非常简单,只需要将库存设置界面执行的SQL:updatestocksetnum=$num_newwheresid=$sid升级为:updatestocksetnum=$num_newwheresid=$sidandnum=$num_old即可。4、什么是ABA问题?CAS乐观锁机制确实可以提高吞吐量和保证一致性,但极端情况下可能会出现ABA问题。ABA问题是什么?考虑以下操作:并发1(上图):获取数据初始值为A,后续计划实现CAS乐观锁。当预期的数据还是A时,就可以修改成功了。并发2:修改数据为B并发3:修改数据回A并发1(下):CAS乐观锁,检测发现初始值还是A,修改数据。A变了,A变B,中间B变A。这个A已经不是A了,但是数据修改成功了,可能会出错。这就是所谓的CAS引起的ABA问题。库存操作、ABA问题不会影响业务。再看另一个栈操作例子:并发1(top):读取栈顶元素为“A1”CAS乐观锁,发现栈顶还是“A1”,所以改为A2,此时会出现系统错误,因为这个“A1”不是另一个“A1”。“价值”只是简单的验证,在某些情况下,相同的“价值”不会引入错误的业务逻辑(如库存),而在某些情况下,“价值”虽然相同,但不再是原来的数据。优化方向:CAS不仅要比较“值”,还必须保证原始数据能够修改成功。常见做法:比较“版本号”,一个版本的数据,版本变化,即使值一样,应该也修改不成功。一个并发读写库存的例子,引入版本号的具体做法如下:(1)库存表由stock(sid,num)升级为stock(sid,num,version)(2)当查询库存,同时查询版本号selectnumfromstockwheresid=$sid升级为selectnum,versionfromstockwheresid=$sid假设有并发操作,会查询版本号sidandnum=$num_old升级为“版本号”,进行比较和CASupdastocksetnum=$num_new,version=$version_newwheresid=$sidandversion=$version_old这时候假设有并发操作,第一次操作比较版本号成功,所以inventory和Versionnumbers都被修改了。同时存在的第二个并发操作,对比的版本号发生了变化,库存应该也修改失败。6、总结一下select&set业务场景,并发时会出现一致性问题。基于“值”的CAS乐观锁可能会导致ABA问题。CAS乐观锁在修改的时候必须保证“这个数据”是“那个数据”,并且应该通过“值”比较来确定,优化为“版本号”比较【本文为专栏作者《58神剑》原稿》,转载请联系原作者】点此查看该作者更多好文