V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
badboy17
V2EX  ›  Java

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

  •  1
     
  •   badboy17 · 2022-09-15 18:34:45 +08:00 · 8137 次点击
    这是一个创建于 560 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    60 条回复    2022-09-19 17:35:05 +08:00
    awalkingman
        1
    awalkingman  
       2022-09-15 18:40:42 +08:00
    ETCD
    sunny1688
        2
    sunny1688  
       2022-09-15 18:41:37 +08:00
    redis 集群不可以吗
    badboy17
        3
    badboy17  
    OP
       2022-09-15 18:46:18 +08:00
    @sunny1688 redis 只用集群的话,有死锁的可能吧,如果一个节点挂了的话,redis 官方提供了 redlock 的分布式锁解决方案,但是这个也有问题,有人质疑这个并不能保证分布式环境下的锁的安全问题
    q474818917
        4
    q474818917  
       2022-09-15 18:46:32 +08:00
    这都 2202 年,还这么卷呢
    wdwwtzy
        5
    wdwwtzy  
       2022-09-15 18:49:11 +08:00
    听听大家的
    GopherDaily
        6
    GopherDaily  
       2022-09-15 18:49:31 +08:00
    @newskillsget etcd 和 zk 在并发上的差距在两个量级以内,所以没有本质差别?

    和这个扯淡的问题没关系
    dwlovelife
        7
    dwlovelife  
       2022-09-15 18:49:49 +08:00
    redisson
    sadfQED2
        8
    sadfQED2  
       2022-09-15 19:03:21 +08:00 via Android   ❤️ 17
    一个搬砖大头兵视角:别他么跟我扯那么多,在我这里,分布式锁就是 redis ,天王老子来了也是 redis 。什么,你说 redis 挂了怎么办? redis 挂了关我屁事,那是运维的锅,反正不是我的锅,挂了直接甩锅给运维
    HunterPan
        9
    HunterPan  
       2022-09-15 19:08:15 +08:00   ❤️ 1
    这种问题 主要考察你的思路,对 redis 和 zk 等的机制优缺点能说出来基本也差不多了。
    Jooooooooo
        10
    Jooooooooo  
       2022-09-15 19:09:47 +08:00
    既要又要吗
    hidemyself
        11
    hidemyself  
       2022-09-15 19:24:46 +08:00   ❤️ 1
    说一下 redis 和 zk 做分布式锁的利弊
    说一下你们为什么,基于什么考虑选择 redis/zk
    就可以了
    badboy17
        12
    badboy17  
    OP
       2022-09-15 19:26:28 +08:00
    @hidemyself 他问的是解决方案,不是问我他们的利弊,利弊他给我介绍了
    PythonYXY
        13
    PythonYXY  
       2022-09-15 19:33:37 +08:00   ❤️ 1
    哪有什么银弹,只能做好业务兜底喽
    bigbyto
        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 内部的实现的逻辑时钟,因此更加安全。
    ClericPy
        15
    ClericPy  
       2022-09-15 20:24:55 +08:00   ❤️ 2
    之前看过各种实现方案, 最后都是改架构不用锁解决的...
    zmal
        16
    zmal  
       2022-09-16 00:01:54 +08:00
    没听说这个 case 有解决方案了啊,有方案踢我一脚。
    用 redis 时做好业务兜底,用 zk 时别要求效率。
    golangLover
        17
    golangLover  
       2022-09-16 00:58:45 +08:00
    @badboy17 不用想了。大部分人没能力解决这个 red lock 的问题
    godfunc
        18
    godfunc  
       2022-09-16 09:19:24 +08:00
    @bigbyto 不懂就问,扣库存的场景中,如果 redis 故障,会导致数据丢失吧
    likeme
        19
    likeme  
       2022-09-16 09:25:09 +08:00
    @ClericPy 不懂就问,不用锁怎么解决高并发带来的问题?
    7911364440
        20
    7911364440  
       2022-09-16 10:01:02 +08:00
    貌似没有完美的解决方案吧,要我选的话就 zookeeper ,毕竟用 zookeeper 实现的分布式锁算是可靠性最高的了,而且有封装好的框架可以用,实现起来也很简单。
    sujin190
        21
    sujin190  
       2022-09-16 10:03:25 +08:00   ❤️ 1
    @bigbyto #14 这分析是不是有点理论派了,加锁大多数情况下只是抑制极限情况下的并发异常,实际性能与冲突率和单操作延时有关,而且实现正常的都是对需要操作的资源加锁而不是对服务加锁,这种情况下并不是全局锁,冲突率实际是极低的,好比 mysql 的表锁和行锁,实际分布式锁实际使用都是行锁,冲突率一般极低才是正常的,所以你这个加锁就是串行过于武断了吧

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


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

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

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

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

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

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

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

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

    我大概测试过 redis ,保守 6w 的原子扣减库存(lua)是很轻松的。
    sujin190
        42
    sujin190  
       2022-09-16 12:05:31 +08:00
    @bigbyto #39 但是事务竞争行锁超过系统限值的问题,无论你用不用分布式锁都是要解决的,我并没有说分布式锁可以同时满足限流逻辑,其解决的仅仅是商品库存校验过程中的并发问题,当然设计合理的分布式锁可以提供限流服务,从系统结构上倒是可以简化有点,我们自己在实现的时候也是充分考虑了我们的流量上限的,而且数据库及其他服务都有连接池限制,超过之后也会 wait ,连接池限制了不可能突破底层数据库和服务的性能限制,所有就直接用使用的异步 IO 框架硬抗了
    bigbyto
        43
    bigbyto  
       2022-09-16 12:09:19 +08:00
    @sujin190 实际上 mysql 数据库的插入性能并不低,只要可以绕过行锁竞争,一台 4 核 8g 的服务器承受 1 秒 6000 条插入游刃有余。加上分表设计,并发下单性能就会非常可观。
    winglight2016
        44
    winglight2016  
       2022-09-16 12:09:42 +08:00
    这不是 CAP 的选择问题吗? AP 或者 CP ?跟需求有关,没有哪个方案能同时满足 CAP
    sujin190
        45
    sujin190  
       2022-09-16 12:12:30 +08:00
    @bigbyto #41 是的,并没有一刀切的通用方案,每个方案都各有优势,就看自己的需求了,如果允许超卖,redis 计库存也挺好的,我提这个方案只是觉得再大多数企业都不可能有美团淘宝这样的量,加锁是实现最直观并且使用产品需求最多又出异常可能最少的方案了

    我们的加锁服务是自己实现的,相同机器上差不多能有 3-5 倍 redis 的 qps ,当然我们用的挺好,也不一定就适合你,开发容易维护简单适合自己需求才是真的

    https://github.com/snower/slock
    sujin190
        46
    sujin190  
       2022-09-16 12:16:50 +08:00
    @bigbyto #43 是的,mysql 的单行单字段的读写性能大概率比大家想的高多了,我们也是想就我们的电商需求不大可能超过 mysql 的单行读写性能,那么就只需解决并发异常就行了,真到了 mysql 都支撑不了了,我们都起飞了,有了资源还怕啥,哈哈
    jack778
        47
    jack778  
       2022-09-16 12:20:19 +08:00   ❤️ 1
    距离面试白板手写操作系统还剩 10 年。
    hhjswf
        48
    hhjswf  
       2022-09-16 15:13:09 +08:00
    cap 理论他不懂吗
    zhaorunze
        49
    zhaorunze  
       2022-09-16 15:19:58 +08:00
    你说的已经是结论了,结论怎么解决呢?
    xz410236056
        50
    xz410236056  
       2022-09-16 15:59:28 +08:00
    @bigbyto #14 Ep = T1 / p*Tp 唤醒了上学的时候计算机系统结构中关于流水线的记忆。。
    zzzzzzZ
        51
    zzzzzzZ  
       2022-09-16 17:14:45 +08:00
    本来就是个破八股,这种没水平的面试官就喜欢强行出截搭题,你这种给制度刷坏了的考生还真就把这截搭题当个宝。


    腐儒,臭不可闻。
    badboy17
        52
    badboy17  
    OP
       2022-09-16 18:03:32 +08:00
    @zzzzzzZ 我就是个初级程序员,面试官是蚂蚁的
    dayeye2006199
        53
    dayeye2006199  
       2022-09-17 01:24:28 +08:00
    好奇你面的这个职位是做什么业务的?
    ly11tea
        54
    ly11tea  
       2022-09-17 08:27:42 +08:00 via iPhone
    @badboy17

    看来你之前没做面试题准备 我刚在 maimai 上搜 最起码 21 年 7 月份就有人面蚂蚁被问到这个问题了
    因该是他们题库里的
    ly11tea
        55
    ly11tea  
       2022-09-17 08:30:26 +08:00 via iPhone
    FrankHB
        56
    FrankHB  
       2022-09-17 13:35:52 +08:00
    @sujin190 “实现正常的都是对需要操作的资源加锁而不是对服务加锁”

    这你可能太乐观了。都不用管啥分布式系统,这年头 GIL 都干不掉的运行时都满大街跑呢……
    (当然搞 Java 的可能没那么倒霉,所以感知不强……)
    sujin190
        57
    sujin190  
       2022-09-17 14:22:05 +08:00 via Android
    @FrankHB 我们现在讨论的是 web 类的无状态服务的状态同步加锁问题,和 gil 本来就不是一回事,和多线程编程也只是类比,并不完全是一回事
    FrankHB
        58
    FrankHB  
       2022-09-17 19:34:24 +08:00
    @sujin190 要怎么给解决方案,自然不是一回事。

    但既然 @bigbyto 有从理论上提到这方面的问题,那我就顺带吐槽一下很多时候很基础的东西在一些情况下会被人莫名其妙地忽略掉。也许是被惯的。
    至少“锁”这个玩意儿什么时候得用,其实这里是不用很关心具体哪个领域的,分析过程和结论都没什么变化。偏偏要直接上具体实例分析,反而无意义地削弱了结论。
    反应得过来的话,就是个简单的锁的粒度的问题……但其实工程中你可能得容忍大多数基础设施的开发者在这里都没怎么拎清楚。

    当然你也可以说,太理论了没必要,对从业人员兼容性太低,反而容易让人联系不到实际问题而抓不住重点。
    这个也说得过去,不过你要提数据库的锁类比,已经相当抽象了。
    而且数据库其实这方面是优等生,这里的期望扩展到其它领域是不那么靠谱的。
    zzzzzzZ
        59
    zzzzzzZ  
       2022-09-19 08:50:45 +08:00
    @badboy17 阿里的也是个臭八股,阿里三四万程序员你以为个个都是人才呢?

    如果你能真正在这行呆下去,过三五年回头看。这都是若智才会问出来的题。

    面新手基础 /算法那么多东西好考,再不济找一些八股看看你理解能力也没问题。强行整这种截搭,暴露出他水平很低劣想在你面前装一波,但是水平有限装个 b 都没装好,属于是架构的门都没摸着的臭腐儒(阿里的)。
    badboy17
        60
    badboy17  
    OP
       2022-09-19 17:35:05 +08:00
    @zzzzzzZ 他问我怎么做幂等,我说用数据库唯一键,然后他说数据库只能是插入场景可以用,修改的场景呢,我说做幂等就建一张幂等表,用业务的唯一 id 做唯一索引,不管是什么业务逻辑要做幂等,都可以用这张表做,我觉得他可能没理解数据库唯一键怎么做幂等,不过我觉得他问我的这个分布式锁的问题,如果让我这个工作了两年不到的程序员回答,有困难,但是我觉得问题有讨论的价值
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1122 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 22:51 · PVG 06:51 · LAX 15:51 · JFK 18:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.