ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java开发和测试开发,面试篇

2021-07-03 15:54:52  阅读:189  来源: 互联网

标签:加锁 Java 获取 Client1 面试 开发 TTL Client 节点


### 分布式锁的坑 **高并发场景下的问题** 以下问题不是说在并发不高的场景下不容易出现,只是在高并发场景下出现的概率更高些而已。 **性能问题来自于以下两方面:** **①获取锁的时间上。**如果 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。 虽然大多数程序员都会乐观的认为这种情况不可能发生,我也曾经这么认为,直到被现实一次又一次的打脸。 ![](http://www.icode9.com/i/li/?n=2&i=images/20210703/1625297169410250.jpg) Martin Kleppmann 也质疑过这一点,这里直接用他的图: * Client1 获取到锁。 * Client1 开始任务,然后发生了 STW 的 GC,时间超过了锁的过期时间。 * Client2 获取到锁,开始了任务。 * Client1 的 GC 结束,继续任务,这个时候 Client1 和 Client2 都认为自己获取了锁,都会处理任务,从而发生错误。 Martin Kleppmann 举的是 GC 的例子,我碰到的是网络延迟的情况。不管是哪种情况,不可否认的是这种情况无法避免,一旦出现很容易懵逼。 如何解决呢?一种解决方案是不设置 TTL,而是在获取锁成功后,给锁加一个 watchdog,watchdog 会起一个定时任务,在锁没有被释放且快要过期的时候会续期。 # 最后 更多Java进阶学习资料、2021大厂面试真题、视频资料可以**[点击这里获取到免费下载方式!](https://docs.qq.com/doc/DSmxTbFJ1cmN1R2dB)** 学习视频: ![](http://www.icode9.com/i/li/?n=2&i=images/20210703/1625297170494876.jpg) 大厂面试真题: ![](http://www.icode9.com/i/li/?n=2&i=images/20210703/1625297170980278.jpg)

标签:加锁,Java,获取,Client1,面试,开发,TTL,Client,节点
来源: https://blog.51cto.com/u_15290981/2973214

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有