【Redis系列】分布式锁

🚀 欢迎来到我的CSDN博客:Optimistic _ chen

一名热爱技术与分享的全栈开发者,在这里记录成长,专注分享编程技术与实战经验,助力你的技术成长之路,与你共同进步!


🚀我的专栏推荐

专栏 内容特色 适合人群
🔥C语言从入门到精通 系统讲解基础语法、指针、内存管理、项目实战 零基础新手、考研党、复习
🔥Java基础语法 系统解释了基础语法、类与对象、继承 Java初学者
🔥Java核心技术 面向对象、集合框架、多线程、网络编程、新特性解析 有一定语法基础的开发者
🔥Java EE 进阶实战 Servlet、JSP、SpringBoot、MyBatis、项目案例拆解 想快速入门Java Web开发的同学
🔥Java数据结构与算法 图解数据结构、LeetCode刷题解析、大厂面试算法题 面试备战、算法爱好者、计算机专业学生
🔥Redis系列 从数据类型到核心特性解析 项目必备

🚀我的承诺:

✅ 文章配套代码:每篇技术文章都提供完整的可运行代码示例

✅ 持续更新:专栏内容定期更新,紧跟技术趋势

✅ 答疑交流:欢迎在文章评论区留言讨论,我会及时回复(支持互粉)


🚀 关注我,解锁更多技术干货!
⏳ 每天进步一点点,未来惊艳所有人!✍️ 持续更新中,记得⭐收藏关注⭐不迷路 ✨

📌 标签:#技术博客#编程学习#Java#C语言#算法#程序员

文章目录

什么是分布式锁

在一个分布式系统中,会涉及到多个节点访问同一个公共资源的场景,此时就需要通过锁来做互斥控制,避免出现"线程安全"问题。

我们多线程中的锁只能在当前线程中生效,在分布式这种多进程多主机的场景下就没有办法了。我们需要使用一个公共服务器来记录加锁状态,这个公共服务器就可以是Redis来支持。

分布式锁的基础实现

基本思路就是通过一个键值对来标识锁的状态。

|------------------------------------|
| 举个例子:买票的时候,车站提供了若干个车次,每个车次的票数都是固定的 |


现在存在多个服务器节点,都可能需要处理这个买票的逻辑,先查询指定车次的余票,如果余票>0,则设置余票 -=1

此时,如果买票服务器尝试买票,就需要先访问Redis,在Redis上设置⼀个键值对.

⽐如key就是⻋次,value随便设置个值。如果这个操作设置成功,就视为当前节点对车次1加锁,可进行数据库的读写操作,操作完成后,再把Redis上的键值对删除。

加入在买票服务器1操作数据库的过程中,买票服务器2也想买票,也会给Redis服务器上写一个键值对,key同样是车次,但是此时发现Redis中已经有该车次的key了,其他节点就会认为有节点正在锁有锁,此时,服务器2就需要等待或者放弃。

注意:Redis中setnx操作刚好适合这个场景,key不存在就设置,存在则设置失败

引入过期时间

当服务器1加锁后,开始进行操作买票,但是如果服务器1意外下线了,就会导致解锁操作(删除key)不能执行,可能会引起其他服务器始终无法获取到锁的情况。

为了解决这个问题,可以在设置key的同时引入过期时间,即这个锁最多持有多久,就应该被释放(set ex nx )注意:该命令只能使用一个命令的方式设置

引入校验ID

对于Redis中写入的加锁键值对,万一其他节点去删除这个键值对的锁呢?

我们可以引入一个校验ID,比如可以把设置的键值对的值,不再随便给一个值,而是设置为买票服务器的编号(或者说是ID),这样就可以删除key(解锁)的时候,先校验当前删除key的服务器是不是当初加锁的服务器,如果是才能真正删除;不是,则不能删除。

引入watch dog(看门狗)

当我们设置了key过期时间之后(⽐如10s),仍然存在⼀定的可能性,当任务还没执⾏完,key就先过期了.这就导致锁提前失效。

治标不治本的方法很简单,把过期时间设置的足够长,但是很明显,设置多⻓时间合适,是⽆⽌境的.即使设置再⻓,也不能完全保证就没有提前失效的情况。所以相比于设置一个固定时间,不如动态的调整时间更合适。

看门狗本质上就是加锁的买票服务器上一个单独的线程,通过这个线程来对锁过期时间进行"加时"。

举个例子:

|---------------------------------------------------------------------------------------------|
| 初始情况下设置过期时间为10s.同时设定看⻔狗线程每隔3s检测⼀次。那么当3s时间到后,看门狗会判定当前任务是否完成:如果任务完成,则直接释放锁;反之,则把过期时间重写设置为10s. |

这样就不担⼼锁提前失效的问题了.⽽且另⼀⽅⾯,如果该服务器挂了,看⻔狗线程也就随之挂了,此时⽆⼈续约,这个key⾃然就可以迅速过期,让其他服务器能够获取到锁了.

引入Redlock算法

实际中Redis一般是以集群的方式部署的,那么也可能出现一下比较极端的情况:

服务器1向master节点进行加锁操作,这个写key的操作刚刚完成,master就挂了;slave节点升级为新的master节点。但是由于刚才写入的key尚未同步给slave,此时相当于服务器1的加锁操作失败了,服务器2仍然可以进行加锁(给新的master写key)

我们引⼊⼀组Redis节点.其中每⼀组Redis节点都包含⼀个主节点和若⼲从节点.并且组和组之间存储的数据都是⼀致的,相互之间是"备份"关系(⽽并⾮是数据集合的⼀部分,这点有别于Redis cluster).

加锁的时候按照一定顺序,写多个master节点,在写锁的时候需要设定操作的"超时时间",如果setnx操作超过了50ms还没有成功,就视为加锁失败。这个节点失败了,就立即尝试下一个节点,当加锁成功的节点超过总数的一半时,才视为加锁成功。

这样的话,即便有些节点挂了,也不影响锁的准确性。记住,释放锁的时候,也需要把所有节点都进⾏解锁操作。

如何保证数据一致性

面试中可能会问Redis和MySQL如何保证双写一致性?也就是当用户修改数据的时候,需要修改数据库,同时也要更新缓存中的数据。

如果直接修改Redis,由于修改Redis和修改MySQL并⾮是原⼦的,多个应⽤服务器并发执⾏的时候,可能就会出现类似于"线程安全"的问题了。

方案一:延时双删

  1. 先删除缓存数据
  2. 再更新数据库
  3. 再次删除缓存数据

两次删除的目的是确保缓存删除成功。
只要把缓存中数据删除,后续访问就会直接访问数据库,并且把结果也写入到Redis中。

如果第二次删除失败了还能怎么办?
方案二:删除缓存重试

  • 先删除缓存数据,如果删除失败,则把失败的key放到⼀个mq中,稍后进⾏重试

完结撒花!🎉

如果这篇博客对你有帮助,不妨点个赞支持一下吧!👍
你的鼓励是我创作的最大动力~

想获取更多干货? 欢迎关注我的专栏 → optimistic_chen

📌 收藏本文,下次需要时不迷路!

我们下期再见!💫 持续更新中......


悄悄说:点击主页有更多精彩内容哦~ 😊

相关推荐
王莽v22 小时前
序列并行-负载均衡
人工智能·分布式
xuekai200809012 小时前
GaussDB-SQL优化案例
数据库·sql·gaussdb
老邓计算机毕设2 小时前
SSM养老院管理系统的设计于实现78fyn(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·计算机毕业设计·养老院管理系统·ssm 框架
a程序小傲2 小时前
京东Java面试被问:基于Gossip协议的最终一致性实现和收敛时间
java·开发语言·前端·数据库·python·面试·状态模式
xiaoliuliu123452 小时前
openssl-libs-1.1.1f-4.p12.ky10.x86_64.rpm 安装指南 解决依赖与常见报错
linux
重生之绝世牛码2 小时前
Linux软件安装 —— PostgreSQL集群安装(主从复制集群)
大数据·linux·运维·数据库·postgresql·软件安装·postgresql主从集群
程序员小白条2 小时前
面试 Java 基础八股文十问十答第二十二期
java·开发语言·数据库·面试·职场和发展·毕设
万象.2 小时前
redis客户端安装与实现C++版本
数据库·c++·redis
Yiyaoshujuku2 小时前
疾病的发病率、发病人数、患病率、患病人数、死亡率、死亡人数查询网站及数据库
数据库·人工智能·算法