真牛逼!我司用了7年的分布式锁方案...

2024-09-02 09:32
文章标签 分布式 方案 我司 真牛

本文主要是介绍真牛逼!我司用了7年的分布式锁方案...,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

点击上方“朱小厮的博客”,选择“设为星标”

后台回复"书",获取

提到数据一致性、操作原子性,诸如此类的一些与并发有关的词汇时不知道你第一时间会联想到什么呢?我相信大多数人可能会想到“锁”,为什么是锁呢,这个我不多说,大家心里应该都明白。在单体应用时代,我们使用jvm提供的锁就可以很好的工作,但是到了分布式应用时代,jvm提供的锁就行不通了,那么势必要借助一些跨jvm的临界资源来支持锁的相关语义,比如redis,zookeeper等。

步入正题

我今天就来分享下我司基于redis来实现的分布式锁,2013年投入使用,也算是久经沙场。但是也存在一些设计上的缺陷,这个我后面也会提到,希望大家秉着互相学习的态度文明交流,别一上来就说这不行那不行,还是那句话“适合自己的才是最好的”。

加锁过程分析

image

我第一次读代码的时候,有这么几个疑惑:

Q1:为什么不使用 SET key value [expiration EX seconds|PX milliseconds] [NX|XX]  这个指令来实现key的自动过期呢,反而放到应用代码判断key是否过期?

A1:我们的分布式锁开发的时候SET命令还不支持NX、PX,所以才想出这种办法来实现key过期,NX、PX在2.6.12以后开始支持;

Q2:已经判断了当前key对应的时间戳已经过期了,为什么还要使用getset再获取一次呢,直接使用set指令覆盖不可以吗?

A2:这里其实牵扯到并发的一些事情,如果直接使用set,那有可能多个客户端会同时获取到锁,如果使用getset然后判断旧值是否过期就不会有这个问题,设想一下如下场景:

  • C1加锁成功,不巧的是,这时C1意外的奔溃了,自然就不会释放锁;

  • C2,C3尝试加锁,这时key已存在,所以C2,C3去判断key是否已过期,这里假设key已经过期了,所以C2,C3使用set指令去设置值,那两个都会加锁成功,这就闯大祸了;如果使用getset指令,然后判断下返回值是否过期就可以避免这种问题,假如C2跑的快,那C3判断返回的时间戳已经过期,自然就加锁失败;

释放锁过程分析

image

Q1:为什么释放锁时还需要判断key是否过期呢,直接del不是性能更高吗?

A1:考虑这样一种场景:

  • C1获取锁成功,开始执行自己的操作,不幸的是C1这时被阻塞了;

  • C2这时来获取锁,由于C1被阻塞了很长时间,所以key对应的value已经过期了,这时C2通过getset加锁成功;

  • C1尘封了太久终于被再次唤醒,对于释放锁这件事它可是认真的,伴随着一波del操作,悲剧即将发生;

  • C3来获取锁,好家伙,居然一下就成功了,接着就是一波操作猛如虎,接着就是一堆的客诉过来了;

为什么会这样呢?回想C1被唤醒以后的事情,居然敢直接del,C2活都没干完呢,锁就被C1给释放了,这时C3来直接就加锁成功,所以为了安全起见C3释放锁时得分成两步:1.判断value是否已经过期 2.如果已过期直接忽略,如果没过期就执行del。这样就真的安全了吗?安全了吗?安全了吗?假如第一步和第二步之间相隔了很久是不是也会出现锁被其他人释放的问题呢?是吧?是的!有没有别的解决办法呢?听说借助lua就可以解决这个问题了,感兴趣的直接给你传送过去可好。

正视自己的缺点

Q1:Redis锁的过期时间小于业务的执行时间该如何续期?

A1:这个暂时没有实现,据说有一个叫Redisson的家伙解决了这个问题,我们也有部分业务在使用,未来有可能会切换到Redisson。

Q2:怎么实现的高可用?

A2:我们采用Failover机制,初始化redis锁的时候会维护一个redis连接池,加锁或者释放锁的时候采用多写的方式来保障一致性,如果某个节点不可用的时候会自动切换到其他节点,但是这种机制可能会导致多个客户端同时获取到锁的情况,考虑这种情况:

  • C1去redis1加锁,加锁成功后会写到redis2,redis3;

  • C2也去redis1加锁,但是此时C2到redis1的网络出现问题,这时C2切换到redis2去加锁,由于第一步中的redis多写并不是原子的,所有就有可能导致C2也获取锁成功;

针对这种情况,目前有些业务方是通过数据库唯一索引的方式来规避的,未来会修复这个bug,具体方案目前还没有。

来源 | https://urlify.cn/iQJ3qa


想知道更多?扫描下面的二维码关注我后台回复"技术",加入技术群【精彩推荐】
  • 超清晰的DNS入门指南

  • 深入理解Java Stream流水线

  • 干掉Swagger,试试这个

  • 干掉GuavaCache:Caffeine才是本地缓存的王

  • 如何用ELK搭建TB级的日志系统

  • 深度好文:Linux系统内存知识

  • 日志系统新贵Loki,确实比笨重的ELK轻

  • 日志采集系统都用到哪些技术?

  • 面试官:为什么HashMap的加载因子是0.75?

  • 原创|OpenAPI标准规范

点个赞+在看,少个 bug ????

这篇关于真牛逼!我司用了7年的分布式锁方案...的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1129600

相关文章

Knife4j+Axios+Redis前后端分离架构下的 API 管理与会话方案(最新推荐)

《Knife4j+Axios+Redis前后端分离架构下的API管理与会话方案(最新推荐)》本文主要介绍了Swagger与Knife4j的配置要点、前后端对接方法以及分布式Session实现原理,... 目录一、Swagger 与 Knife4j 的深度理解及配置要点Knife4j 配置关键要点1.Spri

SQLite3 在嵌入式C环境中存储音频/视频文件的最优方案

《SQLite3在嵌入式C环境中存储音频/视频文件的最优方案》本文探讨了SQLite3在嵌入式C环境中存储音视频文件的优化方案,推荐采用文件路径存储结合元数据管理,兼顾效率与资源限制,小文件可使用B... 目录SQLite3 在嵌入式C环境中存储音频/视频文件的专业方案一、存储策略选择1. 直接存储 vs

SpringBoot服务获取Pod当前IP的两种方案

《SpringBoot服务获取Pod当前IP的两种方案》在Kubernetes集群中,SpringBoot服务获取Pod当前IP的方案主要有两种,通过环境变量注入或通过Java代码动态获取网络接口IP... 目录方案一:通过 Kubernetes Downward API 注入环境变量原理步骤方案二:通过

Springboot3+将ID转为JSON字符串的详细配置方案

《Springboot3+将ID转为JSON字符串的详细配置方案》:本文主要介绍纯后端实现Long/BigIntegerID转为JSON字符串的详细配置方案,s基于SpringBoot3+和Spr... 目录1. 添加依赖2. 全局 Jackson 配置3. 精准控制(可选)4. OpenAPI (Spri

关于跨域无效的问题及解决(java后端方案)

《关于跨域无效的问题及解决(java后端方案)》:本文主要介绍关于跨域无效的问题及解决(java后端方案),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录通用后端跨域方法1、@CrossOrigin 注解2、springboot2.0 实现WebMvcConfig

在Java中将XLS转换为XLSX的实现方案

《在Java中将XLS转换为XLSX的实现方案》在本文中,我们将探讨传统ExcelXLS格式与现代XLSX格式的结构差异,并为Java开发者提供转换方案,通过了解底层原理、性能优势及实用工具,您将掌握... 目录为什么升级XLS到XLSX值得投入?实际转换过程解析推荐技术方案对比Apache POI实现编程

Java实现本地缓存的常用方案介绍

《Java实现本地缓存的常用方案介绍》本地缓存的代表技术主要有HashMap,GuavaCache,Caffeine和Encahche,这篇文章主要来和大家聊聊java利用这些技术分别实现本地缓存的方... 目录本地缓存实现方式HashMapConcurrentHashMapGuava CacheCaffe

Golang实现Redis分布式锁(Lua脚本+可重入+自动续期)

《Golang实现Redis分布式锁(Lua脚本+可重入+自动续期)》本文主要介绍了Golang分布式锁实现,采用Redis+Lua脚本确保原子性,持可重入和自动续期,用于防止超卖及重复下单,具有一定... 目录1 概念应用场景分布式锁必备特性2 思路分析宕机与过期防止误删keyLua保证原子性可重入锁自动

无法启动此程序因为计算机丢失api-ms-win-core-path-l1-1-0.dll修复方案

《无法启动此程序因为计算机丢失api-ms-win-core-path-l1-1-0.dll修复方案》:本文主要介绍了无法启动此程序,详细内容请阅读本文,希望能对你有所帮助... 在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是"api-ms-win-core-path-l1-1-0.dll丢失

基于MongoDB实现文件的分布式存储

《基于MongoDB实现文件的分布式存储》分布式文件存储的方案有很多,今天分享一个基于mongodb数据库来实现文件的存储,mongodb支持分布式部署,以此来实现文件的分布式存储,需要的朋友可以参考... 目录一、引言二、GridFS 原理剖析三、Spring Boot 集成 GridFS3.1 添加依赖