Redis分布式锁实现方式及超时问题解决
作者:知识追寻者 发布时间:2023-08-24 23:28:34
一 前言
redis在分布式应用十分广泛,本篇文章也是互联网面试的重点内容,读者至少需要知道为什么需要分布式锁,分布式锁的实现原理,分布式锁的应用场景,在使用分布式锁时遇到哪些问题?你是如何解决的,如果读者能掌握以上问题,那么关于这道面试题,你也就基本过关了;
二 分布式锁的产生背景
分布式锁对应的是多个应用,每个应用中都可能会处理相同的数据,如果多个应用对用一个操作进行了重复操作,就会出现数据不一致,数据重复问题,于是分布式锁应用而生,通常你可以理解为多线程中的synchronized
三 分布式锁的应用场景
多台机器都能执行某个任务,如果限制任务每次只能被一台机器执行,不能重复执行,就可以用分布式锁来做标记秒杀场景,要求并发量很高,那么同一件商品只能被一个用户抢到,就可以使用分布式锁实现比较敏感的数据比如金额修改,同一时间只能有一个人操作,如果2个人同时修改金额,一个加一个减金额,为了防止同时操作造成数据不一致,就可以使用分布式锁实现
四 分布式锁的实现
4.1 分布式锁的实现方式
基于数据库实现分布式锁
基于缓存(redis,memcached,tair)实现分布式锁
基于Zookeeper实现分布式锁
4.2 分布式锁使用原理
每个应用对敏感数据进行操作时都需要向获取一个锁,持有锁的应用才能对数据进行操作,保证在同一时间内只有一台应用能对数据进行操作;
4.3 分布式锁实现过程
基本实现思路:
redis分布式实现是基于 命令setnx key value , 其意指 若该键不存在则创建键,这就保证了redis中只有一个该键,故应用谁先获得该键,谁就拿到了锁的权限;然后业务逻辑执行完毕则需要使用 del key 删除键,表示释放锁;
出现了问题:
如果一台业务逻辑执行完毕,程序出现异常,则锁会一直存在,没有得到释放,其它应用就会无法获得锁,此时就会造成死锁问题;
改进方式:
拿到锁之后,给锁加上一个过期时间,也就是 expire key seconds 指令;此时避免了死锁问题,但是由于业务逻辑执行的时间不同,过期的时间设置也是一个问题,故通常分布式锁不能应用于业务逻辑执行较长的程序;
出现问题:
由于redis 每条指令都是原子性操作,但由于setnx 和 expire 是2 条指令,如果在执行setnx后程序出现问题expire指令未得到执行就会造成死锁问题;
解决问题:
redis2.8版本之后引入了指令 set key value [EX seconds] [PX milliseconds] [NX|XX] ,该指令可以同时执行 setnx 和 expire ,于是解决了死锁问题;
参数列表解释
EX seconds: 设定过期时间,单位为秒
PX milliseconds: 设定过期时间,单位为毫秒
NX: key不存在时设置值
XX: key存在时设置值
使用jedis客户端实现分布式锁方式
public boolean lock(Jedis jedis,String key,String val,int expireTime){
String lock = jedis.set(key, val, "NX", "PX",
expireTime);
return "OK".equals(lock);
}
关于未获得锁的解决思路:
可以直接抛出异常让客户重试
可以使用延迟队列
五 分布式锁的超时问题
问题:
如果在加锁和释放锁之间,业务逻辑执行时间太长,导致超出了锁的超时限制,就会出现锁过期问题;换句话说,就是第一台应用执行了业务,导致锁过期;第二台应用此时可以获得锁,进行执行业务,此时第一台应用释放了锁,第二台应用在执行业务的时第三台应用获得了锁执行业务,导致在执行过程中,会有2台应用在同时执行业务逻辑;
解决思路:
在释放锁的时候出现了问题,即每台应用都可以释放锁,这会造成1应用的锁释放了2应用锁的问题,换句话说,很多人手中持有的钥匙是通用的,都可以开同一个门;为了避免这个问题,就是1 应用只能释放1应用上的锁,2应用只能释放2应用上的锁,则需要对释放锁进行身份校验;由于上锁的时候key是唯一,但value可以不同,所以可以根据value进行身份的唯一标识,随机数就是一个很好的选择 :
String value = UUID.randomUUID().toString();
由于考虑到匹配到value校验和del不是同一个操作,故需要使用Lua脚本实现多条指令的原子性执行;
jedis释放锁实现方式:
public void unlock(Jedis jedis,String key,String value) {
String script_command = "if redis.call('get',KEYS[1]) == ARGV[1] then " +
"return redis.call('del',KEYS[1]) else return 0 end";
// 解锁
jedis.eval(script_command, Collections.singletonList(key), Collections.singletonList(value));
}
来源:https://www.cnblogs.com/zszxz/p/12488852.html
猜你喜欢
- 一、maven引入依赖,数据库驱动根据项目需求自行引入<!-- https://mvnrepository.com/artifact/
- 最近有小伙伴问我,双枚举类该怎么写,还得包括根据key取值方法。于是就手写一个案例如下:/** * 关系类型枚举 */public enum
- 背景传说里玉皇大帝派龙王马上降雨到共光一带,龙王接到玉皇大帝命令,立马从海上调水,跑去共光施云布雨,但粗心又着急的龙王不小心把海里的鲸鱼随着
- UI 妹纸又给了个图叫我做,我一看是这样的:我们首先把这个控件划分成 几个部分:1.底下部分的直线 :2.左右两边的半圆
- //构造文件File类File f=new File(fileName);//判断是否为目录f.isDirectory();//获取目录下的
- SharedPreferences是Android中最容易理解的数据存储技术,实际上SharedPreferences处理的就是一个key-
- 本文实例讲解了iOS从背景图中取色的代码,分享给大家供大家参考,具体内容如下实现代码:void *bitmapData; //内存空间的指针
- 配置文件中设置通常在公司级别的项目中,我们可能会写多个application- dev/prod.yml ,然后我们通常会在applicat
- 图像滤波在opencv中可以有多种实现形式自定义滤波如使用3×3的掩模:对图像进行处理.使用函数filter2D()实现#include&l
- 本Demo为练手小项目,主要是熟悉目前主流APP的架构模式.此项目中采用MVC设计模式,纯代码和少许XIB方式实现.主要实现了朋友圈功能和摇
- SpringCloud 整合ribbon的时候出现了这个问题java.lang.IllegalStateException: No inst
- 前后端分离开发中,一般都会遇到请求跨域问题。而且一般也会遇到登陆失效问题。今天就以springboot和vue为例来看如何解决上述问题增加过
- [LeetCode] 9. Palindrome Number 验证回文数字Determine whether an integer is
- 开发环境win10Android Studio效果用于多级菜单展示,或选择。如 每个省,市,县;如 树木的病虫害;关键代码 @overrid
- C++虚类相当于java中的抽象类,与接口的不同之处是:1.一个子类只能继承一个抽象类(虚类),但能实现多个接口2.一个抽象类可以有构造方法
- 这篇文章主要介绍了spring boot如何实现切割分片上传,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需
- 1. 前言Spring除了IOC和DI,还有另一个杀手锏功能——Spring AOP。AOP是一种面
- 最近在看《.NET游戏编程入门经典 C#篇》 第一章介绍了如何制作俄罗斯方块,自己试了试按照书上的步骤,可算是完成了。于是写下这篇文章留作纪
- 什么是异步?为什么要用它?异步编程提供了一个非阻塞的,事件驱动的编程模型。 这种编程模型利用系统中多核执行任务来提供并行,因此提供了应用的吞
- 什么是FTPFTP(File Transfer Protocol)是TCP/IP网络上两台计算机传送文件的协议,使得主机间可以共享文件.可以