当前位置:网站首页>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脚本这段代码加了锁 ,所以保证其原子性。
四、总结
为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:
- 互斥性。在任意时刻,只有一个客户端能持有锁。
- 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
- 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
- 加锁和解锁必须具有原子性。
边栏推荐
- Typora颜色公式代码大全
- data:image/jpg;base64格式数据转化为图片
- 全国职业院校技能大赛网络安全竞赛之应急响应
- QT + msvc2017编译器
- 将回调函数转为Flow
- leetcode 22.7.31(1)两数之和 (2)整数除法
- FCN - the originator of semantic segmentation (based on tf-Kersa reproduction code)
- DropBlock: Regularization method and reproduction code for convolutional layers
- 【selenium自动化】第四篇,结合testNg
- 「PHP基础知识」转换数据类型
猜你喜欢

智能健身动作识别:PP-TinyPose打造AI虚拟健身教练!

1161. Maximum Level Sum of a Binary Tree

两日总结七

data:image/jpg;base64格式数据转化为图片

两日总结四
![[想要访问若依后台]若依框架报错401请求访问:error认证失败,无法访问系统资源](/img/aa/701fef9d8d7eaf25082e1289799b77.png)
[想要访问若依后台]若依框架报错401请求访问:error认证失败,无法访问系统资源

【UE虚幻引擎】UE5三步骤实现AI漫游与对话行为

七牛云上传图片和本地上传

Distributed Computing Experiment 4 Random Signal Analysis System

【论文笔记】—低照度图像增强—Supervised—RetinexNet—2018-BMVC
随机推荐
Mysql insert on duplicate key 死锁问题定位与解决
MySQL group_concat()详解
两日总结四
FCN - the originator of semantic segmentation (based on tf-Kersa reproduction code)
图的基本概念
一天搞定JDBC02:开启事务
在GBase 8c数据库后台,使用什么样的命令来对gtm、dn节点进行主备切换的操作?
MMDetection finetune
【JS 逆向百例】某网站加速乐 Cookie 混淆逆向详解
【虚幻引擎UE】UE5基于Gltf加载插件实现gltf格式骨骼动画在线/本地导入和切换
经典动态规划问题的递归实现方法——LeetCode39 组合总和
unity2D横版游戏教程7-敌人AI死亡效果
分布式计算实验3 基于PRC的书籍信息管理系统
LeetCode 135. 分发糖果
千万级别的表分页查询非常慢,怎么办?
小程序如何使用订阅消息(PHP代码+小程序js代码)
LeetCode 97. 交错字符串
【UE虚幻引擎】UE5实现动态导航样条线绘制
Distributed Computing Experiment 3 PRC-based Book Information Management System
unity3d-Animation&&Animator接口(基本使用)