分布式锁
基本原理
定义:分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。
特点:
不同实现方式对比
实现分布式锁时需要实现的两个基本方法————获取、释放
更改后使得语句具有原子性后——
SET lock thread1 NX EX 10
最后还要考虑锁的阻塞与非阻塞问题——这里我们采用非阻塞式锁(即获取失败后不等待直接返回失败)
详见Java基础——线程
以下则是完整的流程与方法——
一、基于setnx的分布式锁
(一)基于Redis实现分布式锁初级版本
需求: 定义一个类,实现下面接口,利用Redis实现分布式锁功能。
实现类——SImpleRedisTemplate.java
实现类详解——
流程图——
获取锁——
释放锁——
测试——VoucherOrderServicelmpl.java
同时用try—finally块包装起来——
有关try——finally知识点请点击这里
同样的启动两台tomcat模拟两台服务器,结果如下——
(同样确保在POSTman中使用同一用户发请求)
Redis键值对——
后台数据——
以上显示分布式锁成功运作!
(二)基于setnx的分布式锁可能出现的问题及解决方案
1、Redis分布式锁误删问题
引出——
归根结底安全问题在于(线程1)在释放锁的时候把别人的锁(线程2)
给删了
解决方案——
获取锁时存入的线程ID,在释放锁时进行对比即可
获取线程ID——
流程图重梳理——
代码修改——
总结——
2、分布式锁原子问题引发的误删问题
问题引出——
因为上面的流程判断锁与释放锁不在同一时刻,在这中间由于JVM的垃圾回收策略等会导致代码执行阻塞,也许又会有执行时间大于EX时间的情况发生,就又出现了上面的多线程并发安全问题。
解决方法——
归根结底是在于判断锁与释放锁不在同一时刻,让其一同执行就好(具有原子性就好)。
lua脚本——保证原子性
lua测试如下——
——含参数脚本语言
用Java语言实现脚本lua
RedisTemplate调用Lua脚本的API如下——
——业务流程
业务代码——
前后对比——
(三)基于setnx的分布式锁总结
二、基于Redisson的分布式锁
——引出,基于setnx分布式锁依然存在的的问题
在之前实现的锁已经能够满足大多数的场景,但是还有可以提升的空间。如下——
有Redisson成熟框架下的支持——
Redisson——在Redis基础下实现的分布式工具集合**(分布式锁是它的子集)**
——以后凡是用到分布式锁时直接用这个开源框架就好
Redisson使用——
1、引入依赖
2、配置
3、使用即可
代码
配置文件RedissonConfig.java
业务类——VoucherOrderServicelmpl.java
(一)Redisson解决setnx不可重入锁的问题
Redisson可重入锁原理
有关详细的可重入机制请点击这里
获取锁逻辑lua——
释放锁逻辑lua——
获取锁系数为2——
退出后重入次数变为1——
debug——trylock源码
(二)Redisson的锁重试和WatchDog机制
重温setnx的锁存在的问题——
上面解决了Redisson解决不可重入的问题,剩下的3个问题Redisson是如何解决的?接下来解析源码解决之——
(三)Redisson解决setnx的不可重试、超时释放问题
详细源码分析教程请点击这里
(四)Redisson获取锁与释放锁的流程和交互——
(五)总结——Redisson分布式锁原理(无主从一致性)
(六)Redisson解决setnx的主从一致性问题multiLock
集群模式下——
#主从节点
存在的问题——
万一主节点宕机后——会从从节点选主,就会出现主从不一致,线程安全问题。
锁失效后就会出现线程安全问题——
Redisson的解决方案——multiLock
——多个节点都是主节点,必须全部主节点的锁都获取才能获取成功
——或者想要更高可用,再在此基础上加主从节点也可
三、分布式锁的总结
总共讲了三种锁,层层递进——
四、剩余问题
当前项目由于使用了大量的分布式锁与数据库的大量交互,虽然保证了一致性,但也造成了性能上的一些缺失。同时由于业务流程是串行的,在一个线程里面执行。解耦性能不好。
接下来就讲解如何在性能上提升——Java项目——黑马点评(优惠券秒杀6之优化秒杀)