• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

java开发校招面试,进阶学习

开发技术 开发技术 3小时前 1次浏览

分布式锁的坑

并发场景下的问题

以下问题不是说在并发不高的场景下不容易出现,只是在高并发场景下出现的概率更高些而已。

性能问题来自于以下两方面:

①获取锁的时间上。如果 Redlock 运用在高并发的场景下,存在 N 个 Master 节点,一个一个去请求,耗时会比较长,从而影响性能。

这个好解决,通过上面描述不难发现,从多个节点获取锁的操作并不是一个同步操作,可以是异步操作,这样可以多个节点同时获取。

即使是并行处理的,还是得预估好获取锁的时间,保证锁的 TTL>获取锁的时间+任务处理时间。

②被加锁的资源太大。加锁的方案本身就是会为了正确性而牺牲并发的,牺牲和资源大小成正比,这个时候可以考虑对资源做拆分。

拆分的方式有如下两种:

①从业务上将锁住的资源拆分成多段,每段分开加锁。比如,我要对一个商户做若干个操作,操作前要锁住这个商户,这时我可以将若干个操作拆成多个独立的步骤分开加锁,提高并发

②用分桶的思想,将一个资源拆分成多个桶,一个加锁失败立即尝试下一个。比如批量任务处理的场景,要处理 200w 个商户的任务,为了提高处理速度,用多个线程,每个线程取 100 个商户处理,就得给这 100 个商户加锁。

如果不加处理,很难保证同一时刻两个线程加锁的商户没有重叠,这时可以按一个维度。

比如某个标签,对商户进行分桶,然后一个任务处理一个分桶,处理完这个分桶再处理下一个分桶,减少竞争。

重试的问题:无论是简单实现还是 Redlock 实现,都会有重试的逻辑。

如果直接按上面的算法实现,是会存在多个 Client 几乎在同一时刻获取同一个锁,然后每个 Client 都锁住了部分节点,但是没有一个 Client 获取大多数节点的情况。

解决的方案也很常见,在重试的时候让多个节点错开,错开的方式就是在重试时间中加一个随机时间。这样并不能根治这个问题,但是可以有效缓解问题,亲试有效。

节点宕机

对于单 Master 节点且没有做持久化的场景,宕机就挂了,这个就必须在实现上支持重复操作,自己做好幂等。对于多 Master 的场景,比如 Redlock,我们来看这样一个场景:

  • 假设有 5 个 Redis 的节点:A、B、C、D、E,没有做持久化。

  • Client1 从 A、B、C 这3 个节点获取锁成功,那么 client1 获取锁成功。

  • 节点 C 挂了。

  • Client2 从 C、D、E 获取锁成功,client2 也获取锁成功,那么在同一时刻 Client1 和 Client2 同时获取锁,Redlock 被玩坏了。

怎么解决呢?最容易想到的方案是打开持久化。持久化可以做到持久化每一条 Redis 命令,但这对性能影响会很大,一般不会采用,如果不采用这种方式,在节点挂的时候肯定会损失小部分的数据,可能我们的锁就在其中。

另一个方案是延迟启动。就是一个节点挂了修复后,不立即加入,而是等待一段时间再加入,等待时间要大于宕机那一刻所有锁的最大 TTL。

但这个方案依然不能解决问题,如果在上述步骤 3 中 B 和 C 都挂了呢,那么只剩 A、D、E 三个节点,从 D 和 E 获取锁成功就可以了,还是会出问题。

那么只能增加 Master 节点的总量,缓解这个问题了。增加 Master 节点会提高稳定性,但是也增加了成本,需要在两者之间权衡。

任务执行时间超过锁的 TTL

之前产线上出现过因为网络延迟导致任务的执行时间远超预期,锁过期,被多个线程执行的情况。

这个问题是所有分布式锁都要面临的问题,包括基于 Zookeeper 和 DB 实现的分布式锁,这是锁过期了和 Client 不知道锁过期了之间的矛盾。

在加锁的时候,我们一般都会给一个锁的 TTL,这是为了防止加锁后 Client 宕机,锁无法被释放的问题。

但是所有这种姿势的用法都会面临同一个问题,就是没法保证 Client 的执行时间一定小于锁的 TTL。

虽然大多数程序员都会乐观的认为这种情况不可能发生,我也曾经这么认为,直到被现实一次又一次的打脸。

java开发校招面试,进阶学习

Martin Kleppmann 也质疑过这一点,这里直接用他的图:

  • Client1 获取到锁。

  • Client1 开始任务,然后发生了 STW 的 GC,时间超过了锁的过期时间。

  • Client2 获取到锁,开始了任务。

  • Client1 的 GC 结束,继续任务,这个时候 Client1 和 Client2 都认为自己获取了锁,都会处理任务,从而发生错误。

Martin Kleppmann 举的是 GC 的例子,我碰到的是网络延迟的情况。不管是哪种情况,不可否认的是这种情况无法避免,一旦出现很容易懵逼。

如何解决呢?一种解决方案是不设置 TTL,而是在获取锁成功后,给锁加一个 watchdog,watchdog 会起一个定时任务,在锁没有被释放且快要过期的时候会续期。

最后

无论是哪家公司,都很重视基础,大厂更加重视技术的深度和广度,面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。

针对以上面试技术点,我在这里也做一些资料分享,希望能更好的帮助到大家。

戳这里免费领取以下资料

java开发校招面试,进阶学习

java开发校招面试,进阶学习

java开发校招面试,进阶学习


程序员灯塔
转载请注明原文链接:java开发校招面试,进阶学习
喜欢 (0)