当前位置:网站首页>redis分布式锁的实现
redis分布式锁的实现
2022-08-04 07:54:00 【零舍】
一、引言
我们在系统中修改已有数据时,需要先读取,然后进行修改保存,此时很容易遇到并发问题。由于修改和保存不是原子操作,在并发场景下,部分对数据的操作可能会丢失。在单服务器系统我们常用本地锁来避免并发带来的问题,然而,当服务采用集群方式部署时,本地锁无法在多个服务器之间生效,这时候保证数据的一致性就需要分布式锁来实现。
二、分布式锁主流的实现方案
基于数据库实现分布式锁
基于缓存(Redis等)
基于Zookeeper
每一种分布式锁解决方案都有各自的优缺点:
性能:redis最高
可靠性:zookeeper最高
三、基于redis实现分布式锁
Redis 锁主要利用 Redis 的 setnx 命令
- 加锁命令:SETNX key value,当键不存在时,对键进行设置操作并返回成功,否则返回失败。KEY 是锁的唯一标识,一般按业务来决定命名。
- 解锁命令:DEL key,通过删除键值对释放锁,以便其他线程可以通过 SETNX 命令来获取锁。
- 锁超时:EXPIRE key second, 设置 key 的超时时间,以保证即使锁没有被显式释放,锁也可以在一定时间后自动释放,避免资源被永远锁住。
上述锁实现方式存在一些问题:
1、 SETNX 和 EXPIRE 非原子性
如果 SETNX 成功,在设置锁超时时间后,服务器挂掉、重启或网络问题等,导致 EXPIRE 命令没有执行,锁没有设置超时时间变成死锁。
解决方案:
1、使用Lua脚本将SETNX 和 EXPIRE组成原子性操作
2、使用set key value ex second nx
2、锁误解除
如果线程 A 成功获取到了锁,并且设置了过期时间 30 秒,但线程 A 执行时间超过了 30 秒,锁过期自动释放,此时线程 B 获取到了锁;随后 A 执行完成,线程 A 使用 DEL 命令来释放锁,但此时线程 B 加的锁还没有执行完成,线程 A 实际释放的线程 B 加的锁。
解决方案:
set获取锁时,设置一个指定的唯一值(例如:uuid),释放前获取这个值,判断是否是自己的锁,如果是,才进行释放锁
3、超时解锁导致并发(类似上一个问题)
如果线程 A 成功获取锁并设置过期时间 30 秒,但线程 A 执行时间超过了 30 秒,锁过期自动释放,此时线程 B 获取到了锁,线程 A 和线程 B 并发执行。
A、B 两个线程发生并发显然是不被允许的,一般有两种方式解决该问题:
- 将过期时间设置足够长,确保代码逻辑在锁释放之前能够执行完成。
- 为获取锁的线程增加守护线程,为将要过期但未释放的锁增加有效时间。
4、删除操作缺乏原子性
如果线程 A 成功获取到了锁,并且设置了过期时间 30 秒,但线程 A 执行时间刚好为 30 秒,锁过期自动释放,此时A刚好完成业务逻辑,并进行了释放锁的判断,但还未释放锁,线程 B又 获取到了锁,随后 线程 A 使用 DEL 命令来释放锁,但此时线程 B 加的锁还没有执行完成,线程 A 实际释放的线程 B 加的锁。
解决方案:
使用Lua脚本保证加锁和释放锁具备原子性。
5、Lua脚本怎么保证其原子性
redis内嵌了一个lua环境(很小)来运行lua脚本,在运行lua脚本时不会运行其他脚步以及命令,类似于给执行lua脚本这段代码加了锁 ,所以保证其原子性。
四、总结
为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:
- 互斥性。在任意时刻,只有一个客户端能持有锁。
- 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
- 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
- 加锁和解锁必须具有原子性。
边栏推荐
猜你喜欢
(三)DDD上下文映射图——老师,我俩可是纯洁的男女关系!
一天学会JDBC06:PrepaerdStatemtnt
经典二分法查找的进阶题目——LeetCode33 搜索旋转排序数组
leetcode 22.7.31(1)两数之和 (2)整数除法
分布式计算实验4 随机信号分析系统
秒懂大模型 | 3步搞定AI写摘要
小程序如何使用订阅消息(PHP代码+小程序js代码)
【JS 逆向百例】某网站加速乐 Cookie 混淆逆向详解
第一次用postgreSQL,想装主从,用的12.7 tar.gz版本。安装好后没在 share目录下找到样例配置recovery.conf.sample,是安装方式不对,还是路径不对?
分布式计算实验1 负载均衡
随机推荐
分布式计算实验4 随机信号分析系统
IntelliJ新建一个类或者包的快捷键是什么?
js-第一个出现两次的字母
字符流与字节流的区别
QT + msvc2017编译器
leetcode 22.8.1 二进制加法
(三)DDD上下文映射图——老师,我俩可是纯洁的男女关系!
GIS数据与CAD数据间带属性字段互相转换还原工具,解决ArcGIS等软件进行GIS数据转CAD数据无法保留属性字段问题
inject() can only be used inside setup() or functional components.
金仓数据库 KDTS 迁移工具使用指南 (7. 部署常见问题)
MYSQL JDBC图书管理系统
New Questions in Module B of Secondary Vocational Network Security Competition
dalle:zero-shot text-to-image generation
华为设备配置VRRP与路由联动监视上行链路
分布式计算实验1 负载均衡
通过GBase 8c Platform安装数据库集群时报错
无人驾驶运用了什么技术,无人驾驶技术是
Linux之Redis 缓存雪崩,击穿,穿透
电脑系统数据丢失了是什么原因?找回方法有哪些?
最近的一些杂感-20220731