最近在学习 java 和 redis ,学到 cas 的部分认识到了 aba 问题。想问一下在生产环境下 aba 具体可能导致什么漏洞呢?我粗略想来感觉 aba 完全不是问题啊,既然“锁”的需求是保证逻辑正常执行(比如计算某变量增加或减少若干,在多线程下不会出错),那么不管怎样 aba ,总归都不会出错不是么
1
liprais 2022-04-14 12:17:54 +08:00 via iPhone
状态 b 呢?
|
2
XiaoxiaoPu 2022-04-14 12:41:37 +08:00
|
3
Richard14 OP @liprais
@XiaoxiaoPu 感谢两位回复,但两位没有回答我的问题啊。比如我预设一个场景,某人账号里有 100 万资产,然后多线程操作重复若干万次随机增加或减少若干值,我感觉对于任意时刻的任意状态下,aba 不会影响这个场景,对于当时间点的计算结果都是准确的,所以 aba 有啥问题 |
4
XiaoxiaoPu 2022-04-14 13:38:34 +08:00
@Richard14
计数只是 CAS 应用的一个场景啊,这个场景没有 aba 问题不代表其他场景没有问题。 |
5
Richard14 OP @XiaoxiaoPu 所以我在问什么场景会出问题
|
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 !!! 此时可以看到,实际余额应该为 100 ( 100-50+50 ),但是实际上变为了 50 ( 100-50+50-50 )这就是 ABA 问题带来的成功提交。 |
7
XiaoxiaoPu 2022-04-14 13:43:44 +08:00
@Richard14
我上面贴的 wiki 的连接给了示例的。。。 |
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 要不要继续操作呢? |
10
vate32 2022-04-14 13:49:38 +08:00
因为 CAS 只关注预期值和当前值的比较,数值实际发生了什么变更,他并不关注。
就比如说,某人账号里有 100 万资产,在存储和提取两个过程中,数额都是一致的,但实际过程中存在有非法挪用并偷偷归还的情况,这明显是违法的。这种场景单纯通过 CAS 读取记录是无法感知到的。 |
12
LeeReamond 2022-04-14 14:01:21 +08:00
@hua123s 理解了一下,可以这么描述,应该理解为小明同时在两台取款机,账号原有 100 块,小明同时发出 2 次取款 100 的请求,理论上应该有一次失败,但如果中途有人汇款 100 那么两次都会成功。
感觉在你描述的这个场景中 cas 不构成问题,毕竟它理论上确实能取出 200 块,但只能说对于一些特殊要求,比如小明实际上只想取款一次,但由于机器错误后台自动发出了两次请求,那么程序是不按期望工作的。(但是实际上小明也没亏。。似乎也没啥问题。。) |
13
yogogo 2022-04-14 14:01:35 +08:00
@AlkTTT 不用 lock For Update 的吗?我目前是 lock For Update 加事务操作,代码加减后再更新余额
|
14
Jooooooooo 2022-04-14 14:17:28 +08:00
一般是钱这种可替换(fungible)的东西 aba 没啥问题, 扣钱又不管扣的哪部分的 100 块.
|
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; |
16
Richard14 OP @hua123s 乐观锁有 ABA 问题是很好理解的,只是好奇生产环境里什么场景需要注意规避 aba 问题,你在上文说的,比如小明想取款一次,但前端错误发出两次请求,后台使用乐观锁在特定条件下(刚好有 IO 发生使两次请求都成功执行),那么后台默认是执行了两次,但两次返回请求前端按正常工作逻辑只能处理第一次,导致小明亏钱。
不过这种场景怎么解决呢,直接用悲观锁吗。 |
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 就好像要自旋,然后再走一遍逻辑,直到成功。 反正就那么个意思吧,我只是一个小前端具体也不是很清楚 |
19
afewok 2022-04-15 01:12:10 +08:00
感觉不一样了,一个洗过澡的女神 /男神出去放浪形骸了一圈回来又洗了个澡,你还心动嘛?
漏洞的话,我想到首次操作不及时,可能这个操作就被放弃,不期望能执行成功,但最后还是执行了。就像 ABA 总用 2 个 ATM 机,第一个 ATM 在你卡都拔了,人都跑到第二台 ATM 机了,突然扣钱成功,拿着钱算不算 离柜概不负责? |