Redis分布式锁的设计与实现
文章目录
参考蚂蚁课堂
1.什么是分布式锁?
本地锁:在多个线程中,保证只有一个线程执行(线程安全的问题)
分布式锁:在分布式中,保证只有一个JVM执行(多个JVM线程安全问题)
举个例子比如说有两台服务器A和B,然后代码打包发布到两台服务器上,万一代码里面有个定时任务调度,这些任务调度和业务逻辑一块发布到两台服务器上假如说这个定时任务是每天晚上半夜2点执行可能会重复执行这时候我们就可以用分布式锁来控制,同一时刻谁拿到锁了谁就执行,没拿到的一边儿站着。
2.分布式锁的实现方案
1.基于数据库方式实现
2.基于zookeeper方式实现:采用的是临时节点+事件通知
3.基于Redis方式实现:setnx命令方式
解决分布式锁的核心思路:
首先我们要获取锁:多个不同的jvm同时创建一个标记(全局唯一的)只要谁能够创建成功谁就能够获取锁。
释放锁就是释放该全局唯一的标记,其他的jvm重新进入到获取锁资源。
超时锁可以分成两种情况第一种是一直没有获取到锁,这个就好比你和你的情敌同时追一个小姑娘(锁),你一直在追(轮询),结果某一天不幸的事情发生了你的情敌跟那个小姑娘好上了(他拿到了锁),这时候你再怎么追也不行了,你一直等一直等这是一个很漫长的过程(这就超时了)你不能一直等所以要放下开启新的人生。
还有一种情况就是获取到了锁一直不撒手,这样别人就拿不到锁,锁一般来说是有有效期的,锁不是私人财产是公共财产,不能只借不还鸠占鹊巢,超过有效期之后锁会自动失效。防止死锁的出现。
3.基于Redis实现分布式锁的方案
- 获取锁:多个不同的jvm同时创建一个相同的标记使用Setnx命令,因为RedisKey必须保证是唯一的,只要谁能够创建成功谁就能够获取锁。
- 释放锁:对我们redis的Key设置一个有效期可以灵活地自动释放该全局唯一标记
- 超时锁:设置等待获取锁的超时时间,已经获取到锁,设置锁的有效期。
Set命令:如果key不存在则创建,如果key已经存在则修改原值
SetNx命令:如果key不存在则创建返回1,如果key已经存在则不执行任何操作返回0。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jQmqMQ2o-1648468458242)(C:\Users\MICROSOFT\Pictures\博客图片\redis\Redis分布式锁\setNx命令.png)]
4.Redis分布式锁的代码实现
为了能够在程序中方便的操作Redis我们引入一个工具类RedisUtil。
public class RedisUtil {
//protected static Logger logger = Logger.getLogger(RedisUtil.class);
private static String IP = "192.168.212.148";
//Redis的端口号
private static int PORT = 6379;
//可用连接实例的最大数目,默认值为8;
//如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
private static int MAX_ACTIVE = 100;
//控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
private static int MAX_IDLE = 20;
//等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
private static int MAX_WAIT = 3000;
private static int TIMEOUT = 3000;
//在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
private static boolean TEST_ON_BORROW = true;
//在return给pool时,是否提前进行validate操作;
private static boolean TEST_ON_RETURN = true;
private static JedisPool jedisPool = null;
/**
* redis过期时间,以秒为单位
*/
public final static int EXRP_HOUR = 60 * 60; //一小时
public final static int EXRP_DAY = 60 * 60 * 24; //一天
public final static int EXRP_MONTH = 60 * 60 * 24 * 30;