问下各位,在分布式系统中,我的模块中的接口属于被调用的,用户的操作不会直接涉及我的模块,一般由其他模块调用我。这种时候我要如何考虑我模块中的并发问题。
我现在负责的会员模板有一个余额消费的功能,由订单模块,调用我会员消费,我只需要将订单模块传给我的消费金额和相关会员账号进行 扣款处理即可,项目经理让我排查一下其中出现的并发场景。但是我不太清楚分布式系统中的,并发问题是怎么产生的。
1
xmh51 2020-01-16 16:15:23 +08:00
别人同一个订单重复调用怎么办?
|
2
fxxkgw 2020-01-16 16:16:22 +08:00 1
对于余额这种感觉说的像是幂等性 至于并发,他是不是只想说流量大的情况下,理论上并发这问题上游有负载,对业务来说压力大扩容就完事了。
|
3
matepi 2020-01-16 16:17:24 +08:00 via iPhone
要你的消费模块做一致性加锁,防止多个调用者并发调你消费的时候超余额消费呗
加锁,tcc 之类的呗 |
4
SpencerCJH 2020-01-16 16:35:03 +08:00
分布式事务一致性、幂等性、分布式锁 了解一下 :D
|
5
wellsc 2020-01-16 16:37:01 +08:00
流量激增,怎么平滑无痛扩容
|
6
xetv 2020-01-16 16:39:58 +08:00 2
同一个订单请求用测试工具并发请求 n 次看余额会不会变成负数
|
7
bobuick 2020-01-16 16:42:54 +08:00 1
单就扣款这个动作看,扣同一个账号下的款,是不是要求就是不能扣成负数?
那就 cas 乐观锁方式处理,或者 db 直接开 select for update 锁 你们扯上 tcc 是什么鬼,lz 只是要保障自己的模块安全就可以了. 要 tcc 之类的,就要联动你的订单模块一起从整体上来看架构了。你们这个没个架构师来把关么,不要随便用 tcc |
8
0NF09LJPS51k57uH 2020-01-16 16:43:01 +08:00 1
4 楼正解,5 楼不知所云。
每个订单的扣款要考虑幂等,不能重复扣款。 模块中如果有多个消费者共享的资源,要考虑加锁或者 CAS,比如领券的场景。 RPC 调用,账户额度和订单状态要考虑一致性。 |
9
Renco OP 对于分布式系统来说 sychronize 锁是不是没有意义
|
10
0NF09LJPS51k57uH 2020-01-16 17:00:37 +08:00 1
@Renco Redis、Zookeeper 是比较常见的分布式锁实现方案。如果你们有现成的 redis 集群,可以了解一下 Redisson,借助 Redis 完全依照 Java 规范实现包括分布式锁在内的各种分布式工具。
借助其他实现了分布式一致性协议的组件也可以实现分布式锁。 |
11
justfly 2020-01-16 17:01:52 +08:00 2
但是我不太清楚分布式系统中的,并发问题是怎么产生的。
就你这个场景,两个对同一个用户的扣款请求,可能被路由到你服务的两个完全不相关的进程上,这两个进程内各自处理自己的扣款操作,比如是检查余额和扣款俩操作,二者均检查本次扣款余额充足没问题,结果可能会扣款变成负数。这时候就需要分布式锁来强制两个用户的扣款操作顺序进行。 |
14
wangyzj 2020-01-16 17:08:13 +08:00
tcc
|
15
notwaste 2020-01-16 19:15:35 +08:00 via Android
好多大佬,学习了
|
16
index90 2020-01-16 20:10:18 +08:00 1
跟分布式系统没多大关系吧
即使你是单点的也会有并发问题啊 例如,我用同一个账号同时下了两个单 例如,我消费的同时对账号进行充值 对共享数据(余额)进行并发读写,要注意的是脏读脏写问题 |
17
FrankD 2020-01-16 20:47:01 +08:00 via Android
楼里瞎扯啥,一堆名词往上怼,人家说的是这个场景的吗
|
18
opengps 2020-01-16 21:12:31 +08:00
关键词:分布式锁。
多了不敢说,毕竟我经验不足 |
19
skypyb 2020-01-16 21:51:14 +08:00
来,给你两篇分布式锁的文章,一个 Redis 的,一个 Zookeeper 的
http://zhangtielei.com/posts/blog-redlock-reasoning.html http://skypyb.com/2019/08/jishu/943/ |
20
CoderGeek 2020-01-16 21:56:48 +08:00
一般情况:
1.redis、zk、数据库行锁 2.是否幂等 3.条件如 version 版本号、余额是否可为负等 |
21
CoderGeek 2020-01-16 21:58:20 +08:00
看你描述已经流转到你内部系统了 不要跑偏了
|
22
CoderGeek 2020-01-16 22:03:49 +08:00
对了哦我说这些是单个用户才需要考虑的哦,多个用户间不互转不存在并发问题
|
23
wccc 2020-01-16 23:40:49 +08:00 via iPhone
加版本号即可,保持接口的幂等
|
24
wysnylc 2020-01-17 03:30:39 +08:00 via Android 1
分布式锁性能差,做多队列单消费者
保证一个用户任务在一个队列中 上面要加锁的一律不看,并发的终极方案就是队列 队列分组就 hash 取余,后期可升级一致性哈希环 |
25
btnokami 2020-01-17 07:04:27 +08:00
所以你的模块只是作为 Proxy 来处理 request 发给 database,还是 databse 那一层呢。
感觉把并发问题推给 database layer 然后用不同的 isolation level 来处理会比较简单 |
26
btnokami 2020-01-17 07:06:51 +08:00
分布式计算就两个终极问题,exactly once delivery 和 consensus。。。往上套就好了
|
28
lewis89 2020-01-17 07:32:34 +08:00
@wysnylc #24 很显然 楼主的场景跟楼主公司的业务量并没有那么大的规模,如果有的话 肯定不会轮到写代码的来排查这个问题了,在架构设计上就要避免了..
|
29
lewis89 2020-01-17 07:39:11 +08:00
@wysnylc #24 我看它的情况,会员系统应该本身应该就维护了 扣款 跟 会员权限配置 的事务操作,这本身就是一个本地事务,如果别人调你的话,你只要对外保证幂等性即可,一般分布式调用链会有 SpanId 调用 用来维护调用链的(像 SpringCloud-Seluth ),最好对 SpanId 做一个幂等性,有些中间件会在超时的时候做一些重试操作,很有可能单个订单会调用两次扣款操作
|
30
lewis89 2020-01-17 07:41:49 +08:00
或者可以针对订单 ID 做幂等性校验 把订单 ID 本地落库跟会员扣款放到同一个本地事务 commit 进数据库,做好隔离级别校验跟订单 ID 唯一性校验,如果有重复订单 ID 落库 直接吞掉异常 正常返回即可
|
31
passerbytiny 2020-01-17 09:27:27 +08:00 2
前方在极短的时间内连续调用你的接口,这个时候你的接口,可能是多节点同时执行,也可能是单节点多线程同时执行,但访问的数据库只有一个:这就是并发场景。这种场景,单节点应用和分布式应用都会出现。
这个场景你如果不考虑,那么可能出现的问题是:一,没上事务,余额表的变动历史将乱成一锅粥;二,上了事务,但在扣余额的时候没做二次验证,余额会被口称负的。 解决方法如下,顺序是从简单到复杂,所有方法都要先有事务(单节点事务): 一、乐观锁,只适合单节点应用。这个太常见了,而且你也不是单节点应用,就不细说了。 二、实时动态扣费,适合订单模块(不是你的模块)并发量不是特别高的情况。你在扣费的时候不直接扣,先去查下当前余额,如果扣完不为负,再扣费。你的接口的返回结果不是 void,也不是 true/false,而是一个对象,包含这些字段:是否扣款成功、扣费前的实际余额、扣费后的实际余额。 三、最终一致性,适合订单模块超高并发量的情况。直接扣费,返回结果总是成功,如果扣成负的了,发事件通知,让其他模块去做补偿(或者啥也不干就允许余额为负,或者再通知订单模块去取消订单)。 最后说一句,看见分布式事务或者分布式锁的回复,请直接忽略,这是老早就被淘汰的技术。 |
32
passerbytiny 2020-01-17 09:38:01 +08:00
还有各种回复“幂等”的,你们在想什么。首先,这是接口调用不是事件订阅,没有重发和无序性,无需考虑幂等;其次,扣余额这种动作你做幂等? 10 天前扣 1 元跟今天扣 1 元的结果一摸一样?
|
33
ke1e 2020-01-17 09:51:37 +08:00 via Android
最简单的就是模拟并发情况,看会出现什么问题,然后再去解决
|
34
xuanbg 2020-01-17 10:08:30 +08:00
楼主你只需要考虑你的模块有多个服务实例的情况下,每个实例都在同一个时间扣同一个用户的钱的时候,如何保证扣款正确。
|
35
bukeshuo 2020-01-17 10:39:41 +08:00
数据库乐观锁 或者 直接 分布式锁 锁账号
|
36
Raymon111111 2020-01-17 10:42:41 +08:00
。。。
楼里很多人是不是根本没有做过业务,只看过书,一堆什么分布式锁都出来了 这是要梳理场景,不是解决方案。何况场景没有,解决方案是怎么意淫出来的 并发问题主要考虑两点,这个接口短时间相同请求发起两次请求,这个接口被不同请求同时调用 |
37
leafre 2020-01-17 11:15:17 +08:00 via iPhone
@passerbytiny 好吧 我们最近上线的项目就是使用被淘汰的分布式锁
|
38
peyppicp 2020-01-17 11:29:47 +08:00 1
修改为订单模式,每笔余额扣减都有一个固定单号,需要明确一个状态机,保证状态流转正确
做业务时,开启事务,select for update 锁住对应的单号,用户的余额,然后在本事务中做业务,完事了 commit 这种同一单号的并发请求过来,一定会被挂起,执行代码中增加判断订单状态的逻辑就好了,如果本单状态为成功则幂等返回成功。 |
39
peyppicp 2020-01-17 11:31:26 +08:00
这个感觉压根没必要上到分布式系统的层面,也没有必要考虑分布式锁,用 db 抗住就完事了。
你这是有热点会员吗,同一个会员做大量余额扣减?如果是这样完全可以考虑用子会员的方法实现,也不差的 |
40
wc951 2020-01-17 12:03:32 +08:00 via Android
扣款表里把订单号加唯一索引
|