CAS 乐观锁如果发生 ABA,可能导致什么问题? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Richard14
V2EX    问与答

CAS 乐观锁如果发生 ABA,可能导致什么问题?

  •  
  •   Richard14 2022-04-14 12:10:59 +08:00 2329 次点击
    这是一个创建于 1365 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在学习 java 和 redis ,学到 cas 的部分认识到了 aba 问题。想问一下在生产环境下 aba 具体可能导致什么漏洞呢?我粗略想来感觉 aba 完全不是问题啊,既然“锁”的需求是保证逻辑正常执行(比如计算某变量增加或减少若干,在多线程下不会出错),那么不管怎样 aba ,总归都不会出错不是么

    19 条回复    2022-04-15 01:12:10 +08:00
    F281M6Dh8DXpD1g2
        1
    F281M6Dh8DXpD1g2  
       2022-04-14 12:17:54 +08:00 via iPhone
    状态 b 呢?
    XiaoxiaoPu
        2
    XiaoxiaoPu  
       2022-04-14 12:41:37 +08:00
    Richard14
        3
    Richard14  
    OP
       2022-04-14 13:32:42 +08:00
    @liprais
    @XiaoxiaoPu 感谢两位回复,但两位没有回答我的问题啊。比如我预设一个场景,某人账号里有 100 万资产,然后多线程操作重复若干万次随机增加或减少若干值,我感觉对于任意时刻的任意状态下,aba 不会影响这个场景,对于当时间点的计算结果都是准确的,所以 aba 有啥问题
    XiaoxiaoPu
        4
    XiaoxiaoPu  
       2022-04-14 13:38:34 +08:00
    @Richard14
    计数只是 CAS 应用的一个场景啊,这个场景没有 aba 问题不代表其他场景没有问题。
    Richard14
        5
    Richard14  
    OP
       2022-04-14 13:41:58 +08:00
    @XiaoxiaoPu 所以我在问什么场景会出问题
    hua123s
        6
    hua123s  
       2022-04-14 13:42:41 +08:00
    小明在提款机,提取了 50 元,因为提款机问题,有两个线程,同时把余额从 100 变为 50

    线程 1 (提款机):获取当前值 100 ,期望更新为 50 ,
    线程 2 (提款机):获取当前值 100 ,期望更新为 50 ,
    线程 1 成功执行,线程 2 某种原因 block 了,这时,某人给小明汇款 50

    线程 3 (默认):获取当前值 50 ,期望更新为 100 ,
    这时候线程 3 成功执行,余额变为 100 , 线程 2 从 Block 中恢复,获取到的也是 100 ,compare 之后,继续更新余额为 50 !!! 此时可以看到,实际余额应该为 10 ( 100-50+50 ),但是实际上变为了 50 ( 100-50+50-50 )这就是 ABA 问题带来的成功提交。
    XiaoxiaoPu
        7
    XiaoxiaoPu  
       2022-04-14 13:43:44 +08:00
    @Richard14
    我上面贴的 wiki 的连接给了示例的。。。
    AlkTTT
        8
    AlkTTT  
       2022-04-14 13:43:51 +08:00
    现在我要发起两个操作,1.支付 20w ; 2.转入 20w
    由于各种问题,同时有两个线程执行这两个操作
    线程 1: 查到资产为 100w ( A )
    线程 2: 查到资产为 100w ( A )
    支付 20w ,转入 20w ( B )
    总金额 100w ( A )
    问:线程 1 要不要继续操作呢?
    yogogo
        9
    yogogo  
       2022-04-14 13:48:22 +08:00
    @AlkTTT 像这样操作钱包余额更新,你们是数据库 SQL 执行加减,还是在代码层面执行加减之后再更新到数据库?
    vate32
        10
    vate32  
       2022-04-14 13:49:38 +08:00
    因为 CAS 只关注预期值和当前值的比较,数值实际发生了什么变更,他并不关注。
    就比如说,某人账号里有 100 万资产,在存储和提取两个过程中,数额都是一致的,但实际过程中存在有非法挪用并偷偷归还的情况,这明显是违法的。这种场景单纯通过 CAS 读取记录是无法感知到的。
    AlkTTT
        11
    AlkTTT  
       2022-04-14 13:51:21 +08:00
    @yogogo 第二种,我们是用 reids 锁资源的,不能用 sql 操作
    LeeReamond
        12
    LeeReamond  
       2022-04-14 14:01:21 +08:00
    @hua123s 理解了一下,可以这么描述,应该理解为小明同时在两台取款机,账号原有 100 块,小明同时发出 2 次取款 100 的请求,理论上应该有一次失败,但如果中途有人汇款 100 那么两次都会成功。

    感觉在你描述的这个场景中 cas 不构成问题,毕竟它理论上确实能取出 200 块,但只能说对于一些特殊要求,比如小明实际上只想取款一次,但由于机器错误后台自动发出了两次请求,那么程序是不按期望工作的。(但是实际上小明也没亏。。似乎也没啥问题。。)
    yogogo
        13
    yogogo  
       2022-04-14 14:01:35 +08:00
    @AlkTTT 不用 lock For Update 的吗?我目前是 lock For Update 加事务操作,代码加减后再更新余额
    Jooooooooo
        14
    Jooooooooo  
       2022-04-14 14:17:28 +08:00
    一般是钱这种可替换(fungible)的东西 aba 没啥问题, 扣钱又不管扣的哪部分的 100 块.
    hua123s
        15
    hua123s  
       2022-04-14 14:43:27 +08:00
    @LeeReamond 我也觉得例子不好懂。
    首要前提:认为乐观锁没有 ABA 问题。
    线程 1:update table set balance = 50 where balance = 100;
    线程 2:update table set balance = 50 where balance = 100;
    当前背景下,1 ,2 是幂等的,执行多少次都无所谓啦。
    但是实际上会有 ABA 问题,
    线程 1 是 where balance = 100 and version = 1;
    线程 2 需要执行是 where balance = 100 and version = 1; 结果执行的是 where balance = 100 and version = 2;
    Richard14
        16
    Richard14  
    OP
       2022-04-14 15:00:20 +08:00
    @hua123s 乐观锁有 ABA 问题是很好理解的,只是好奇生产环境里什么场景需要注意规避 aba 问题,你在上文说的,比如小明想取款一次,但前端错误发出两次请求,后台使用乐观锁在特定条件下(刚好有 IO 发生使两次请求都成功执行),那么后台默认是执行了两次,但两次返回请求前端按正常工作逻辑只能处理第一次,导致小明亏钱。

    不过这种场景怎么解决呢,直接用悲观锁吗。
    AlkTTT
        17
    AlkTTT  
       2022-04-14 15:12:21 +08:00
    @yogogo lock For Update 只能锁单表,业务上是多表,所以在代码里发锁进行操作
    hua123s
        18
    hua123s  
       2022-04-14 15:26:06 +08:00
    @Richard14 加版本号重试。
    举个栗子:
    select id, version from table;

    if(xxx){
    update table set xxx, version = version + 1 where id = 1 and version = 1; // version = verison + 1
    }

    如果 affect rows 不对,即其实 version 被其他线程改了,affect rows 就是 0
    就好像要自旋,然后再走一遍逻辑,直到成功。
    反正就那么个意思吧,我只是一个小前端具体也不是很清楚
    afewok
        19
    afewok  
       2022-04-15 01:12:10 +08:00
    感觉不一样了,一个洗过澡的女神 /男神出去放浪形骸了一圈回来又洗了个澡,你还心动嘛?
    漏洞的话,我想到首次操作不及时,可能这个操作就被放弃,不期望能执行成功,但最后还是执行了。就像 ABA 总用 2 个 ATM 机,第一个 ATM 在你卡都拔了,人都跑到第二台 ATM 机了,突然扣钱成功,拿着钱算不算 离柜概不负责?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5307 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 03:40 PVG 11:40 LAX 19:40 JFK 22:40
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86