网站首页 > java教程 正文
起因
如果在一个分布式系统中,我们很多业务场景。因为读取和更新保存不是一个原子操作,在并发时就会导致数据的不正确。这种场景其实并不少见,比如电商秒杀活动,库存数量的更新就会遇到。如果是单机应用,直接使用本地锁就可以避免。如果是分布式应用,本地锁派不上用场,这时就需要引入分布式锁来解决。
为了保证分布式锁的可用性,至少要确保锁的实现要同时满足以下几点:
- 互斥性。在任何时刻,保证只有一个客户端持有锁。
- 不能出现死锁。持有锁的客户端挂死能自动释放。
- 保证上锁和解锁都是同一个客户端。
实现方式
一般来说,实现分布式锁的方式有以下几种:
- 使用MySQL,基于唯一索引,实现简单(如SeaTa、xxx-job)
- 使用ZooKeeper,基于临时有序节点
- 使用Redis,基于setnx命令。
但为了追求更好的性能,我们通常会选择使用 Redis 或 Zookeeper 来做,我们这里主要聊聊redis的实现。
- 简单实现(实现互斥性)
想要实现分布式锁,必须要求 Redis 有锁的能力,能让多个客户端实现互斥,我们可以使用 SETNX 命令,这个命令表示set if not exists,即如果 key 不存在,才会设置它的值,否则什么也不做。
客户端1:setnx mylock 1
客户端2:setnx mylock 1
两个客户端去执行这个命令只会有一个会成功,获取成功的客户端就可以去执行相关的资源操作,操作完成后再进行删除:DEL mylock
但是,这个只实现了互斥性,死锁是没办法避免的,比如客户端1获取锁成功了,处理资源的过程中挂死了,没有进行删除mylock,此时就会产生死锁。
- 避免死锁
第一步:
为了避免死锁,给获取锁定一个有效时间,过期则自动失效,redis本来就有过期淘汰策略,可以比较好集成:
客户端:setnx mylock 1
客户端:expire mylock 30
这样,如果30秒之内没反应则自动释放这一把锁,顺提一句,这种在面试的过程中,经常会问到用了redis分布式锁之后怎么避免死锁的问题是很经常见的。
但是这样依然还会有问题,因为两条命令不是原子性的,可能出现设置时间失败,依旧存在死锁的可能,只是比之前减低了一些概率。
第二步:
redis里面有multi与exec可以保证两条命令一起执行,如下图:
但是这样使用还会出现其它的问题,两条命令是一起执行的,打个比方:
客户端1:执行上面代码,然后挂死了
客户端2:执行上面代码,没拿到锁,但是更新了过期时间
客户端3:执行上面代码,没拿到锁,但是更新了过期时间
客户端n:.......................................
锁一直没释放,客户端挂死没有自动释放锁,如果其它客户端访问频率较高,锁会一直不被释放,产生死锁。
第三步:
Redis 2.6.12 之后,Redis 扩展了 SET 命令的参数,用这一条命令就可以了:SET mylock 1 EX 10 NX;这样就解决了死锁问题,也比较简单,没有了第二步里面的问题,不会自动刷新过期时间。
但是这个还有两个问题
1.锁超时,处理资源所耗时间超过30秒,自动释放锁,会出现两个客户端同时操作公共资源
2.释放锁,当出现两个客户端操作公共资源后,第一个客户端去删除锁,会把后来的客户端加的锁给释放掉了
第四步:
1.Redisson 是一个 Java 语言实现的 Redis SDK 客户端,在使用分布式锁时,它就采用了「自动延期」的方案来避免锁过期,这个守护线程我们一般也把它叫做「看门狗」线程。
2.客户端在加锁时,设置一个只有自己知道的一个值进去,获取判断是自己的值再进行删除,那么get和del命令又得保持原子性,可以使用watch、multi、exec来保证。
总结
死锁:加过期时间
锁过期时间用完:redission守护线程,自动续期
锁被别人释放:锁写入唯一标识,释放锁先检查标识,再释放
猜你喜欢
- 2024-11-22 4K字深度剖析redisson分布式锁原理
- 2024-11-22 java都为我们提供了各种锁,为什么还需要分布式锁?
- 2024-11-22 一文带你了解Java手写分布式锁的实现
- 2024-11-22 如何用Redisson框架实现分布式锁?
- 2024-11-22 基于 Redis 实现的分布式锁
- 2024-11-22 京东秒杀系统模块的Redis分布式锁深度剖析,没给你讲明白你打我
- 2024-11-22 面试官:Redis分布式锁超时了,任务还没执行完怎么办?
- 2024-11-22 聊聊Redis分布式锁
- 2024-11-22 浅谈分布式锁
- 2024-11-22 分布式锁系列一:基于Redis SETNX命令实现分布式锁
你 发表评论:
欢迎- 最近发表
-
- 五,网络安全IDA Pro反汇编工具初识及逆向工程解密实战
- 「JAVA8」- Lambda 表达式(java lambda表达式原理)
- 深入探讨Java代码保护:虚拟机保护技术的新时代
- Nginx反向代理原理详解(图文全面总结)
- 逆向拆解日本IT,哪些Java技术栈薪资溢价高
- mybatis 逆向工程使用姿势不对,把表清空了,心里慌的一比
- Spring Boot集成ProGuard轻松实现Java 代码混淆, Java 应用固若金汤
- 从 Java 代码逆向工程生成 UML 类图和序列图
- 人与人相处:尊重是标配,靠谱是高配,厚道是顶配
- Windows系统安装日期如何修改(windows10怎么修改安装日期)
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)