Redis实现分布式锁的几种方案
欢迎各位小伙伴来到golang学习网,相聚于此都是缘哈哈哈!今天我给大家带来《Redis实现分布式锁的几种方案》,这篇文章主要讲到分布式、Redis、锁等等知识,如果你对数据库相关的知识非常感兴趣或者正在自学,都可以关注我,我会持续更新相关文章!当然,有什么建议也欢迎在评论留言提出!一起学习!
1.前言
对于Redis实现分布式锁的几种方案这个话题,展开之前我想先简单聊聊什么是分布式锁,分布式锁的使用场景,除了Redis外还有什么技术实现分布式锁等一系列内容。
1.1分布式锁
说大一点,就是在现在发展越来越迅速的大背景下,去中心化分布式系统越来越普及,在我们实际的生产开发当中,有一种不可避免的场景就是多个进程互斥的对其资源的使用,为了保证数据不重复,要求在同一时刻,同一任务只在一个节点上运行,且保证在多进程下的数据安全,分布式锁就十分重要了。
1.2分布式锁的几种方案
方式有很多种,根据技术角度的不同
有基于MySQL的方式,通过表的唯一索引,通过insert和delete就可以实现加锁和解锁的效果;
有基于zookeeper的方式,通过创建临时有序节点,判断创建的节点序号是否最小。若是,则表示获取到锁,不是,则watch /lock目录下序号比自身小的前一个节点,解锁只需要删除节点;
有基于Redis的方式。通过执行setnx,若成功再执行expire添加过期时间的方式加锁,解锁执行delete命令。
方式有很多,不一一列举了。
1.3Redis分布式锁需要满足的条件
- 互斥性。在任意时刻,只有一个客户端能持有锁。
- 不发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁也能保证后续其他客户端能加锁。
- 同一性。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了,即不能误解锁。
- 容错性。只要大多数Redis节点正常运行,客户端就能够获取和释放锁。
2.Redis实现分布式锁的几种方案
可以通过以下方式实现(包括但不限于):
- SETNX + EXPIRE
- SETNX + value(系统时间+过期时间)
- 通过开源框架-Redisson
简单来说说,用Java代码演示:
2.1 SETNX + EXPIRE
setnx(SET IF NOT EXISTS)+ expire命令。先用setnx来抢锁,如果抢到锁,再用expire给锁设置一个过期时间,这样持有锁超时时释放锁,防止锁忘记释放。但此时setnx和expire两个命令无法保证原子性,例如:
if(jedis.setnx(key_resource_id,lock_value) == 1){ //加锁
expire(key_resource_id,100); //设置过期时间
try {
//业务代码块
}catch() {
}finally {
jedis.del(key_resource_id); //释放锁
}
}
2.2 SETNX + value(系统时间+过期时间)
可以把过期时间放到setnx的value值里面。如果加锁失败,再拿出value值校验一下即可。加锁代码如下:
long expires = System.currentTimeMillis() + expireTime; //系统时间+设置的过期时间
String expiresStr = String.valueOf(expires);
// 如果当前锁不存在,则加锁成功
if (jedis.setnx(key_resource_id, expiresStr) == 1) {
return true;
}
// 如果锁已经存在,获取锁的过期时间
String currentValueStr = jedis.get(key_resource_id);
// 如果获取到的过期时间,小于系统当前时间,表示已经过期
if (currentValueStr != null && Long.parseLong(currentValueStr) // 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间
String oldValueStr = jedis.getSet(key_resource_id, expiresStr);
if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
// 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才可以加锁
return true;
}
}
//其他情况均返回加锁失败
return false;
2.3 通过开源框架-Redisson
那么此时就要去想了,如果已经超过了加锁的过期时间,可是业务还没执行完成,这个时候怎么做呢?是把过期时间延长吗?显然不合理,可以通过开源框架-Redisson优化这个问题,简单来说,Redisson就是当一个线程获得锁以后,给该线程开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。假设两个线程争夺统一公共资源:线程A获取锁,并通过哈希算法选择节点,执行Lua脚本加锁,同时其看门狗机制会启动一个watch dog(后台线程),每隔10秒检查线程,如果线程A还持有锁,那么就会不断的延长锁key的生存时间。线程B获得锁失败,就会订阅解锁消息,当获取锁到剩余过期时间后,调用信号量方法阻塞住,直到被唤醒或等待超时。一旦线程A释放了锁,就会广播解锁消息。于是,解锁消息的监听器会释放信号量,获取锁被阻塞的线程B就会被唤醒,并重新尝试获取锁。
Redisson 支持单点模式、主从模式、哨兵模式、集群模式,假设现为单点模式:
//构造Config
Config config = new Config();
config.useSingleServer().setAddress("redis://ip:port").setPassword("Password.~#").setDatabase(0);
//构造RedissonClient
RedissonClient redissonClient = Redisson.create(config);
//获取锁实例
RLock rLock = redissonClient.getLock(lockKey);
try {
//获取锁,waitTimeout为最大等待时间,超过这个值,则认为获取锁失败。leaseTime为锁的持有时间
boolean res = rLock.tryLock((long)waitTimeout, (long)leaseTime, TimeUnit.SECONDS);
if (res) {
//业务块
}
} catch (Exception e) {
}finally{
//解锁
rLock.unlock();
}
3.小结
Redis的分布式锁实现方式有很多,这里不一一列举了,有机会再展开Lua脚本、分布式锁Redlock等内容。
以上就是《Redis实现分布式锁的几种方案》的详细内容,更多关于redis的资料请关注golang学习网公众号!
一文带你了解MySQL是如何优化in子查询的,其实非常简单
- 上一篇
- 一文带你了解MySQL是如何优化in子查询的,其实非常简单
- 下一篇
- Redis的IO多路复用以及Select,Epoll的演进
-
- 忧郁的灯泡
- 这篇博文真是及时雨啊,好细啊,写的不错,已加入收藏夹了,关注老哥了!希望老哥能多写数据库相关的文章。
- 2023-03-21 08:20:36
-
- 刻苦的裙子
- 这篇技术贴太及时了,老哥加油!
- 2023-03-21 07:49:16
-
- 懦弱的睫毛膏
- 真优秀,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢楼主分享博文!
- 2023-03-14 23:11:39
-
- 眯眯眼的热狗
- 太全面了,码住,感谢up主的这篇博文,我会继续支持!
- 2023-03-07 00:11:48
-
- 数据库 · Redis | 3小时前 |
- Redis HSETNX 怎样只在 hash 字段不存在时写入初始值
- 191浏览 收藏
-
- 数据库 · Redis | 3小时前 |
- Redis BLMOVE 怎样阻塞式转移可靠队列元素
- 445浏览 收藏
-
- 数据库 · Redis | 3小时前 |
- Redis LMPOP 怎样从多个列表选择一个非空来源
- 473浏览 收藏
-
- 数据库 · Redis | 3小时前 | Redis · Stream ·
- Redis XACK 怎样避免重复确认消费者组消息
- 374浏览 收藏
-
- 数据库 · Redis | 3小时前 |
- Redis XTRIM 怎样按 MINID 保留指定范围的 Stream
- 379浏览 收藏
-
- 数据库 · Redis | 3小时前 |
- Redis XAUTOCLAIM 怎样转交空闲消费者的消息
- 215浏览 收藏
-
- 数据库 · Redis | 3小时前 | Redis · hash ·
- Redis HTTL 怎样查询 hash 字段剩余时间
- 354浏览 收藏
-
- 数据库 · Redis | 3小时前 |
- Redis EXPIRE 怎样只在已有 TTL 时缩短过期时间
- 377浏览 收藏
-
- 数据库 · Redis | 3小时前 | Redis · 缓存 · redis 随机抽样 有序集合 WITHSCORES ZRANDMEMBER
- Redis ZRANDMEMBER 怎样随机抽取有序集合成员
- 375浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 666次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 684次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 730次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 698次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 532次使用
-
- Go语言实战之实现一个简单分布式系统
- 2022-12-23 220浏览
-
- Go与Redis实现分布式互斥锁和红锁
- 2022-12-22 117浏览
-
- golang 基于 mysql 简单实现分布式读写锁
- 2023-01-07 384浏览
-
- go 分布式锁简单实现实例详解
- 2022-12-28 130浏览
-
- Go+Redis实现延迟队列实操
- 2023-02-23 426浏览

