又被面试官问倒了,关于分布式锁

2022-09-15 18:34:45 +08:00
 badboy17

今天面试官问我,他们用 redis 和 zookeeper 都用做过分布式锁,但是 redis 做分布式锁有高可用的问题,zookeeper 的能提供的并发量也有限,怎么解决这个问题 来 v 社问问大佬们,怎么思考

8259 次点击
所在节点    Java
60 条回复
sujin190
2022-09-16 10:03:25 +08:00
@bigbyto #14 这分析是不是有点理论派了,加锁大多数情况下只是抑制极限情况下的并发异常,实际性能与冲突率和单操作延时有关,而且实现正常的都是对需要操作的资源加锁而不是对服务加锁,这种情况下并不是全局锁,冲突率实际是极低的,好比 mysql 的表锁和行锁,实际分布式锁实际使用都是行锁,冲突率一般极低才是正常的,所以你这个加锁就是串行过于武断了吧

@ClericPy #15 分布式锁无非解决的就是状态一致,相同的结果确实有很多解决方案,但是从工程实现来说,如果有一个延迟和可靠性都很好的分布式锁服务,似乎这个是实现和维护最简单的方案了吧,加锁逻辑毕竟已经在多线程编程中实际实践验证过了
wangyzj
2022-09-16 10:11:07 +08:00
强一致性和最终一致性的问题
redis 性能好
但量特别大的情况下还得做一些架构调整
zk 做锁真的可以吗?
sujin190
2022-09-16 10:16:54 +08:00
zookeeper 和 etcd 的核心功能应该是仲裁,加锁当然也可以认为是一种仲裁逻辑,但是吧有点现实经验的都知道,申请仲裁虽然可靠但肯定不会效率高,实际 web 类项目中,估计大部分场景需要分布式锁的估计只是状态同步而不是仲裁
NeoZephyr
2022-09-16 10:21:44 +08:00
用 redis 就不要想高可用,用 zk etcd 就不要想高性能
javaisthebest
2022-09-16 10:33:56 +08:00
@badboy17
1. 锁过期,定时释放就好了
2. 高可用,Redis Cluster 集群完全 hold 得住
3. 同一把锁, 业务代码做好前置校验,通过唯一索引或者其他来保证。


还有问题?
xaplux
2022-09-16 10:39:23 +08:00
要性能就别加锁
momocraft
2022-09-16 10:43:24 +08:00
如果只有一个东西需要锁 用一个队列逐个执行可能更简单
bigbyto
2022-09-16 11:01:00 +08:00
@sujin190

"实现正常的都是对需要操作的资源加锁而不是对服务加锁,这种情况下并不是全局锁,冲突率实际是极低的"
这一点我请教一下,你们实际锁的使用是如何设计来达到你描述的冲突低的。 实际上有很多场景我们操作资源并不是对单一资源的操作,如果对多个资源分别加锁,这就很容易出现死锁。
BBCCBB
2022-09-16 11:05:45 +08:00
zk 实现锁, 也会被网络抖动影响, 网络抖动, 被 zk 判定为离线, 临时节点删除, 导致别的实例也获取到锁.. 就会有多个同时执行??
bigbyto
2022-09-16 11:06:51 +08:00
@BBCCBB 服务在释放锁的时候就能感知到锁不属于自己了
fengpan567
2022-09-16 11:12:41 +08:00
分布式锁?问就是 redission
BBCCBB
2022-09-16 11:16:22 +08:00
@bigbyto 但他的代码都执行完了.. 除非这个方法里面的操作全都支持事务.. 能回滚.
maocat
2022-09-16 11:21:37 +08:00
解决方案就是,不要因为一个问题而引入新的问题
sujin190
2022-09-16 11:21:54 +08:00
@bigbyto #28 比如外卖下单对商品库存加锁这个逻辑吧,我们正常使用分布式锁的逻辑应该对商品 ID 加锁,虽然美团这样的平台每天订单海量,但是商品也是海量的啊,普通场景下对单个商品的瞬间下单请求并会很高吧

对于多个资源那就更简单了,把多个资源的 ID 拼成一个 key 来加锁就行吧,简单来说,我们使用分布式锁是对“{唯一操作类型}+{join(所有该操作设计的资源 ID)}” 这样一个 key 加锁吧

顺便说,在下单这样场景中,下单操作就是唯一操作类型,库存操作才是,资源 ID 自然就是商品 ID ,对商品 ID 的加锁应该只持续了对这个商品校验生成订单的商品信息这一小步,而不是整个下单流程,如果最后又下单失败应该走事务回滚逻辑,而不是让加锁持续整个下单逻辑,否则比如下单时选择的商品顺序不一致你就要死锁了,这样一看,是不是就算时电商下单这样的逻辑上其实冲突率都并不会很高
bigbyto
2022-09-16 11:34:52 +08:00
@sujin190 再请教下你们实际场景有这样用过吗,对性能的影响有没有经过详细的测试验证。

想一下你提到的美团场景,实际上美团商家经常会有一些特价商品,如果是一些热门商家,多个用户都会选择这个特价商品再加上其他商品,如果是这种场景,这个热门商家的锁几乎是被穿透了?大量的请求都获得了锁,是否会把下游的服务击穿?
liuxingdeyu
2022-09-16 11:40:14 +08:00
感觉应该反问场景。如果是日常就高,而且对锁可用性要求高就应该业务上拆,然后想办法横向拉宽。如果是抢购的话,那就一部分直接拒,筛一部分再进来走逻辑
roundgis
2022-09-16 11:43:31 +08:00
分佈式鎖的最佳實踐不就是

不要用它
sujin190
2022-09-16 11:46:03 +08:00
@bigbyto #35 是的,我们的电商服务就是这么设计的,虽然 qps 不算高,高的时候也就上千,不过使用上一致都是很稳定的,因为加锁逻辑相比乐观锁或者队列串行之类还是直观不少,维护和产品逻辑变更感觉上还是相对比较容易的

热点商品是否能支持的很好,其实这个应该和你服务整体支持的性能容量有关吧,如果你整体服务性能容量不足,你怎么着都无法处理这么多下单请求,这个地方应该时只需要考虑你分布式锁会不会成为整个订单服务的性能不足点就可以了

关于大量的请求都获得了锁把下游的服务击穿,你确定这个不是应该由限流、熔断服务及削峰填谷策略来解决的么,和是否使用分布式锁来处理关系不大吧
bigbyto
2022-09-16 11:52:16 +08:00
@sujin190 不知道你们有没有压测的数据,这种设计在一定的节点下能承载多高的并发下单量?其实我想表达的是,如果大量请求传到了下游,就会出现多个事务竞争行锁的问题,这样系统的吞吐量就会急剧下降。
sujin190
2022-09-16 11:55:36 +08:00
@bigbyto #35 顺便说,现在不都喜欢用异步 IO 框架么,针对热点商品,因为冲突率上升导致等待时间增长,那么自身就组成了一个虚拟的队列,而且这个队列无状态自恢复高性能,自身就具备很好的削峰填谷的效果,毕竟美团这种就算是热点商品一天估计也就能卖几千几万的,等待时间订单顶多也就是半分钟,还是在 loading 等待期限之内的吧

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/880339

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX