今天面试官问我,他们用 redis 和 zookeeper 都用做过分布式锁,但是 redis 做分布式锁有高可用的问题,zookeeper 的能提供的并发量也有限,怎么解决这个问题 来 v 社问问大佬们,怎么思考
1
awalkingman 2022-09-15 18:40:42 +08:00
ETCD
|
2
sunny1688 2022-09-15 18:41:37 +08:00
redis 集群不可以吗
|
3
badboy17 OP @sunny1688 redis 只用集群的话,有死锁的可能吧,如果一个节点挂了的话,redis 官方提供了 redlock 的分布式锁解决方案,但是这个也有问题,有人质疑这个并不能保证分布式环境下的锁的安全问题
|
4
q474818917 2022-09-15 18:46:32 +08:00
这都 2202 年,还这么卷呢
|
5
wdwwtzy 2022-09-15 18:49:11 +08:00
听听大家的
|
6
GopherDaily 2022-09-15 18:49:31 +08:00
|
7
dwlovelife 2022-09-15 18:49:49 +08:00
redisson
|
8
sadfQED2 2022-09-15 19:03:21 +08:00 via Android 17
一个搬砖大头兵视角:别他么跟我扯那么多,在我这里,分布式锁就是 redis ,天王老子来了也是 redis 。什么,你说 redis 挂了怎么办? redis 挂了关我屁事,那是运维的锅,反正不是我的锅,挂了直接甩锅给运维
|
9
HunterPan 2022-09-15 19:08:15 +08:00 1
这种问题 主要考察你的思路,对 redis 和 zk 等的机制优缺点能说出来基本也差不多了。
|
10
Jooooooooo 2022-09-15 19:09:47 +08:00
既要又要吗
|
11
hidemyself 2022-09-15 19:24:46 +08:00 1
说一下 redis 和 zk 做分布式锁的利弊
说一下你们为什么,基于什么考虑选择 redis/zk 就可以了 |
12
badboy17 OP @hidemyself 他问的是解决方案,不是问我他们的利弊,利弊他给我介绍了
|
13
PythonYXY 2022-09-15 19:33:37 +08:00 1
哪有什么银弹,只能做好业务兜底喽
|
14
bigbyto 2022-09-15 20:15:45 +08:00 25
不管是使用 redis 还是 zookeeper 作为分布式锁,结果必然是把所有并行请求串行化,这必然会带来一个非常严重的性能损失。如果获得锁的线程需要 10ms 完成一个任务,那么 10 个并发请求必然需要 100ms ,所以他说的“zookeeper 的能提供的并发量也有限”我表示怀疑,到底是他系统先到瓶颈,还是 zookeeper 先到瓶颈?
对于一个并行系统执行效率的评估,可以引用 csapp 中的一条公式 Ep = T1 / p*Tp Ep: 相对效率 T1: 单线程执行时间 p: 线程数 Tp: p 个线程的执行时间 对于他的系统,解决单线程的执行效率才是最重要的,这就意味着不能串行化,要放弃使用锁。所以就需要评估他们业务系统对于锁的依赖有多重,是不是业务重要到必须不能出任何差错。 就拿扣库存这种业务场景,可以通过库存预热到 redis ,通过 redis 原子扣减库存,再通过异步任务把库存回写到数据库,达到数据的最终一致。 还有一点,redis 最致命的问题在于它不是可靠的锁,它依赖了系统时钟,在学术角度看,在某些情况下它是没法判断 2 个 event 之间的 happend-before 关系。 而 zookeeper 内部的实现的逻辑时钟,因此更加安全。 |
15
ClericPy 2022-09-15 20:24:55 +08:00 2
之前看过各种实现方案, 最后都是改架构不用锁解决的...
|
16
zmal 2022-09-16 00:01:54 +08:00
没听说这个 case 有解决方案了啊,有方案踢我一脚。
用 redis 时做好业务兜底,用 zk 时别要求效率。 |
17
golangLover 2022-09-16 00:58:45 +08:00
@badboy17 不用想了。大部分人没能力解决这个 red lock 的问题
|
20
7911364440 2022-09-16 10:01:02 +08:00
貌似没有完美的解决方案吧,要我选的话就 zookeeper ,毕竟用 zookeeper 实现的分布式锁算是可靠性最高的了,而且有封装好的框架可以用,实现起来也很简单。
|
21
sujin190 2022-09-16 10:03:25 +08:00 1
|
22
wangyzj 2022-09-16 10:11:07 +08:00
强一致性和最终一致性的问题
redis 性能好 但量特别大的情况下还得做一些架构调整 zk 做锁真的可以吗? |
23
sujin190 2022-09-16 10:16:54 +08:00 1
zookeeper 和 etcd 的核心功能应该是仲裁,加锁当然也可以认为是一种仲裁逻辑,但是吧有点现实经验的都知道,申请仲裁虽然可靠但肯定不会效率高,实际 web 类项目中,估计大部分场景需要分布式锁的估计只是状态同步而不是仲裁
|
24
NeoZephyr 2022-09-16 10:21:44 +08:00 1
用 redis 就不要想高可用,用 zk etcd 就不要想高性能
|
25
javaisthebest 2022-09-16 10:33:56 +08:00
|
26
xaplux 2022-09-16 10:39:23 +08:00
要性能就别加锁
|
27
momocraft 2022-09-16 10:43:24 +08:00
如果只有一个东西需要锁 用一个队列逐个执行可能更简单
|
28
bigbyto 2022-09-16 11:01:00 +08:00
@sujin190
"实现正常的都是对需要操作的资源加锁而不是对服务加锁,这种情况下并不是全局锁,冲突率实际是极低的" 这一点我请教一下,你们实际锁的使用是如何设计来达到你描述的冲突低的。 实际上有很多场景我们操作资源并不是对单一资源的操作,如果对多个资源分别加锁,这就很容易出现死锁。 |
29
BBCCBB 2022-09-16 11:05:45 +08:00
zk 实现锁, 也会被网络抖动影响, 网络抖动, 被 zk 判定为离线, 临时节点删除, 导致别的实例也获取到锁.. 就会有多个同时执行??
|
31
fengpan567 2022-09-16 11:12:41 +08:00
分布式锁?问就是 redission
|
33
maocat 2022-09-16 11:21:37 +08:00 via iPhone
解决方案就是,不要因为一个问题而引入新的问题
|
34
sujin190 2022-09-16 11:21:54 +08:00
@bigbyto #28 比如外卖下单对商品库存加锁这个逻辑吧,我们正常使用分布式锁的逻辑应该对商品 ID 加锁,虽然美团这样的平台每天订单海量,但是商品也是海量的啊,普通场景下对单个商品的瞬间下单请求并会很高吧
对于多个资源那就更简单了,把多个资源的 ID 拼成一个 key 来加锁就行吧,简单来说,我们使用分布式锁是对“{唯一操作类型}+{join(所有该操作设计的资源 ID)}” 这样一个 key 加锁吧 顺便说,在下单这样场景中,下单操作就是唯一操作类型,库存操作才是,资源 ID 自然就是商品 ID ,对商品 ID 的加锁应该只持续了对这个商品校验生成订单的商品信息这一小步,而不是整个下单流程,如果最后又下单失败应该走事务回滚逻辑,而不是让加锁持续整个下单逻辑,否则比如下单时选择的商品顺序不一致你就要死锁了,这样一看,是不是就算时电商下单这样的逻辑上其实冲突率都并不会很高 |
35
bigbyto 2022-09-16 11:34:52 +08:00
@sujin190 再请教下你们实际场景有这样用过吗,对性能的影响有没有经过详细的测试验证。
想一下你提到的美团场景,实际上美团商家经常会有一些特价商品,如果是一些热门商家,多个用户都会选择这个特价商品再加上其他商品,如果是这种场景,这个热门商家的锁几乎是被穿透了?大量的请求都获得了锁,是否会把下游的服务击穿? |
36
liuxingdeyu 2022-09-16 11:40:14 +08:00
感觉应该反问场景。如果是日常就高,而且对锁可用性要求高就应该业务上拆,然后想办法横向拉宽。如果是抢购的话,那就一部分直接拒,筛一部分再进来走逻辑
|
37
roundgis 2022-09-16 11:43:31 +08:00 via Android
分佈式鎖的最佳實踐不就是
不要用它 |
38
sujin190 2022-09-16 11:46:03 +08:00
@bigbyto #35 是的,我们的电商服务就是这么设计的,虽然 qps 不算高,高的时候也就上千,不过使用上一致都是很稳定的,因为加锁逻辑相比乐观锁或者队列串行之类还是直观不少,维护和产品逻辑变更感觉上还是相对比较容易的
热点商品是否能支持的很好,其实这个应该和你服务整体支持的性能容量有关吧,如果你整体服务性能容量不足,你怎么着都无法处理这么多下单请求,这个地方应该时只需要考虑你分布式锁会不会成为整个订单服务的性能不足点就可以了 关于大量的请求都获得了锁把下游的服务击穿,你确定这个不是应该由限流、熔断服务及削峰填谷策略来解决的么,和是否使用分布式锁来处理关系不大吧 |
39
bigbyto 2022-09-16 11:52:16 +08:00
@sujin190 不知道你们有没有压测的数据,这种设计在一定的节点下能承载多高的并发下单量?其实我想表达的是,如果大量请求传到了下游,就会出现多个事务竞争行锁的问题,这样系统的吞吐量就会急剧下降。
|
40
sujin190 2022-09-16 11:55:36 +08:00
@bigbyto #35 顺便说,现在不都喜欢用异步 IO 框架么,针对热点商品,因为冲突率上升导致等待时间增长,那么自身就组成了一个虚拟的队列,而且这个队列无状态自恢复高性能,自身就具备很好的削峰填谷的效果,毕竟美团这种就算是热点商品一天估计也就能卖几千几万的,等待时间订单顶多也就是半分钟,还是在 loading 等待期限之内的吧
|
41
bigbyto 2022-09-16 12:02:42 +08:00
@sujin190 我个人的观点是只有在十分严谨的场景才会考虑用锁(比如涉及到金额),对于下单的场景,使用 redis 之类的设计并发量能提升好几个数量级,这个缺点就是在极端情况下超卖和少卖选择一个。
我大概测试过 redis ,保守 6w 的原子扣减库存(lua)是很轻松的。 |
42
sujin190 2022-09-16 12:05:31 +08:00
@bigbyto #39 但是事务竞争行锁超过系统限值的问题,无论你用不用分布式锁都是要解决的,我并没有说分布式锁可以同时满足限流逻辑,其解决的仅仅是商品库存校验过程中的并发问题,当然设计合理的分布式锁可以提供限流服务,从系统结构上倒是可以简化有点,我们自己在实现的时候也是充分考虑了我们的流量上限的,而且数据库及其他服务都有连接池限制,超过之后也会 wait ,连接池限制了不可能突破底层数据库和服务的性能限制,所有就直接用使用的异步 IO 框架硬抗了
|
43
bigbyto 2022-09-16 12:09:19 +08:00
@sujin190 实际上 mysql 数据库的插入性能并不低,只要可以绕过行锁竞争,一台 4 核 8g 的服务器承受 1 秒 6000 条插入游刃有余。加上分表设计,并发下单性能就会非常可观。
|
44
winglight2016 2022-09-16 12:09:42 +08:00
这不是 CAP 的选择问题吗? AP 或者 CP ?跟需求有关,没有哪个方案能同时满足 CAP
|
45
sujin190 2022-09-16 12:12:30 +08:00
@bigbyto #41 是的,并没有一刀切的通用方案,每个方案都各有优势,就看自己的需求了,如果允许超卖,redis 计库存也挺好的,我提这个方案只是觉得再大多数企业都不可能有美团淘宝这样的量,加锁是实现最直观并且使用产品需求最多又出异常可能最少的方案了
我们的加锁服务是自己实现的,相同机器上差不多能有 3-5 倍 redis 的 qps ,当然我们用的挺好,也不一定就适合你,开发容易维护简单适合自己需求才是真的 https://github.com/snower/slock |
46
sujin190 2022-09-16 12:16:50 +08:00
@bigbyto #43 是的,mysql 的单行单字段的读写性能大概率比大家想的高多了,我们也是想就我们的电商需求不大可能超过 mysql 的单行读写性能,那么就只需解决并发异常就行了,真到了 mysql 都支撑不了了,我们都起飞了,有了资源还怕啥,哈哈
|
47
jack778 2022-09-16 12:20:19 +08:00 1
距离面试白板手写操作系统还剩 10 年。
|
48
hhjswf 2022-09-16 15:13:09 +08:00
cap 理论他不懂吗
|
49
zhaorunze 2022-09-16 15:19:58 +08:00
你说的已经是结论了,结论怎么解决呢?
|
50
xz410236056 2022-09-16 15:59:28 +08:00
@bigbyto #14 Ep = T1 / p*Tp 唤醒了上学的时候计算机系统结构中关于流水线的记忆。。
|
51
zzzzzzZ 2022-09-16 17:14:45 +08:00
本来就是个破八股,这种没水平的面试官就喜欢强行出截搭题,你这种给制度刷坏了的考生还真就把这截搭题当个宝。
腐儒,臭不可闻。 |
53
dayeye2006199 2022-09-17 01:24:28 +08:00
好奇你面的这个职位是做什么业务的?
|
54
ly11tea 2022-09-17 08:27:42 +08:00 via iPhone
|
55
ly11tea 2022-09-17 08:30:26 +08:00 via iPhone
|
56
FrankHB 2022-09-17 13:35:52 +08:00
@sujin190 “实现正常的都是对需要操作的资源加锁而不是对服务加锁”
这你可能太乐观了。都不用管啥分布式系统,这年头 GIL 都干不掉的运行时都满大街跑呢…… (当然搞 Java 的可能没那么倒霉,所以感知不强……) |
57
sujin190 2022-09-17 14:22:05 +08:00 via Android
@FrankHB 我们现在讨论的是 web 类的无状态服务的状态同步加锁问题,和 gil 本来就不是一回事,和多线程编程也只是类比,并不完全是一回事
|
58
FrankHB 2022-09-17 19:34:24 +08:00
@sujin190 要怎么给解决方案,自然不是一回事。
但既然 @bigbyto 有从理论上提到这方面的问题,那我就顺带吐槽一下很多时候很基础的东西在一些情况下会被人莫名其妙地忽略掉。也许是被惯的。 至少“锁”这个玩意儿什么时候得用,其实这里是不用很关心具体哪个领域的,分析过程和结论都没什么变化。偏偏要直接上具体实例分析,反而无意义地削弱了结论。 反应得过来的话,就是个简单的锁的粒度的问题……但其实工程中你可能得容忍大多数基础设施的开发者在这里都没怎么拎清楚。 当然你也可以说,太理论了没必要,对从业人员兼容性太低,反而容易让人联系不到实际问题而抓不住重点。 这个也说得过去,不过你要提数据库的锁类比,已经相当抽象了。 而且数据库其实这方面是优等生,这里的期望扩展到其它领域是不那么靠谱的。 |