1 高并发场景下的困难
1.1 典型付出场景
这是最经典的场景。付出过程,要先查询买家的账户余额,然后计算商品价格,末了对买家进行进行扣款,像这类的分布式利用,如果是并发量低的情况下完全没有问题的,但如果是并发扣款,那可能就有一致性问题。在高并发的分布式业务场景中,雷同这种 “查询+修改” 的利用很可能导致数据的不一致性。
1.2 在线下单场景
同理,买家在电商平台下单,通常会涉及到两个动作,一个是扣库存,第二个是更新订单状态,库存和订单一样平常属于不同的数据库,需要利用分布式事务包管数据一致性。
1.3 跨行转账场景
跨行转账问题也是一个典型的分布式事务,用户A同学向B同学的账户转账500,要先辈行A同学的账户-500,然后B同学的账户+500,既然是 不同的银行,涉及不同的业务平台,为了包管这两个利用步调的一致,数据一致性方案必然要被引入。
2 CAS方案
分布式CAS(Compare-and-Swap)模式就是一种无锁化头脑的应用,它通过无锁算法实现线程间对共享资源的无冲突访问,既包管性能高效,有包管数据的强一致性,制止了上面会集问题的产生。CAS模式包含三个根本利用数:内存地址V、旧的预期值A和要修改的新值B。在更新一个变量的时候,只有当变量的预期值A和内存地址V当中的现实值相同时,才会将内存地址V对应的值修改为B。
我们以 1.1节 的 典型付出场景 作为例子分析(参考下图):
- 初始余额为 800
- 业务1和业务2同时查询余额为800
- 业务1实验购买利用,扣减去100,结果是700,这是新的余额。理论上只有在原余额为800时,扣减的Action才气实验乐成。
- 业务2实验生活缴费利用(比如自动交电费),原余额800,扣减去200,结果是600,这是新的余额。理论上只有在原余额为800时,扣减的Action才气实验乐成。可现实上,这个时候数据库中的金额已经变为600了,以是业务2的并发扣减不应该乐成。
根据上面的CAS原理,在Swap更新余额的时候,加上Compare条件,跟初始读取的余额比较,只有初始余额不变时,才允许Swap乐成,这是一种常见的降低读写锁冲突,包管数据一致性的方法。
3 引出ABA问题
在CAS(Compare-and-Swap)利用中,ABA问题是一个常见的挑战。这边假设三个利用数——内存位置(V)、预期原值(A)和新值(B)。ABA问题是指当某个线程读取一个共享变量V的值为A,之后准备将其更新为B时,另一个线程可能已经将其从A改为了B,然后又改回了A。此时,当火线程仍以为V的值是原始的A,因此CAS利用会将V的值更新为B,但现实上V的值已经被其他线程改变过。
它有如下危害:
1. 数据一致性受损,并导致业务逻辑错误在复杂的业务逻辑中,共享变量的值通常代表了某种业务状态或条件。ABA问题可能导致这些状态或条件被意外地改变,从而引发业务逻辑错误,如库存超卖、资金重复发放等。★ 以下的图详细形貌了ABA是怎么导致库存逻辑出错的:
2. 难以调试与定位ABA问题通常发生在多线程环境下,且其触发条件较为隐蔽。因此,当系统出现由ABA问题导致的异常时,通常难以快速定位问题原因,增加了调试的复杂性和时间成本。
4 不同维度的处理方式
ABA出现的原因,是CAS的过程中,只关注Value值的校验。但是忽略了这个值照旧不是之前的那个值,可以参考上面的库存图例。以是某些情况下,Value虽然相同,却已经不是原来的数据了。
解决方案:CAS不能只比对 Value,还必须确保的是原来的数据,才气修改乐成。一样平常的做法是,给 Value 设置一个Version(版本号),用来比对,一个数据一个版本,每次数据变化的时候版本跟随变化,如许的话就不会随任意便修改乐成。
4.1 应用步伐层
Java中的java.util.concurrent.atomic包提供相识决ABA问题的工具类。在Go语言中,通常利用sync/atomic包提供的原子利用来处理并发问题,并引入版本号或时间戳的概念。示例代码如下:
- type ValueWithVersion struct {
- Value int32
- Version int32
- }
-
- var sharedValue atomic.Value // 使用atomic.Value来存储ValueWithVersion的指针
-
- func updateValue(newValue, newVersion int32) bool {
- current := sharedValue.Load().(*ValueWithVersion)
- if current.Value == newValue && current.Version == newVersion {
- // CAS操作:只有当前值和版本号都匹配时,才更新值
- newValueWithVersion := &ValueWithVersion{Value: newValue, Version: newVersion + 1}
- sharedValue.Store(newValueWithVersion)
- return true
- }
- return false
- }
复制代码
4.2 数据层
1、CAS策略
- update stock set num_val=$num_new_val where sid=$sid and num_val=$num_old_val
复制代码
2、CAS策略+Version,制止ABA问题
- # 这边注意,有了version,就没必要再比较old_val了
- update stock set num=$num_new_val, version=$version_new where sid=$sid and version=$version_old
复制代码
5 总结
- 高并发下的困难:付出、下单、跨行转账
- CAS方案以及引发的ABA问题
- 不同维度的处理方式:应用层、数据层
文章转载自:Hello-Brand
原文链接:https://www.cnblogs.com/wzh2010/p/18031157
体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |