当前位置:网站首页>Interviews are no longer hanged!This is the correct way to open the seven schemes of Redis distributed locks
Interviews are no longer hanged!This is the correct way to open the seven schemes of Redis distributed locks
2022-08-03 16:40:00 【InfoQ】
前言
- 什么是分布式锁
- 方案一:SETNX + EXPIRE
- 方案二:SETNX + value值是(系统时间+过期时间)
- 方案三:使用Lua脚本(包含SETNX + EXPIRE两条指令)
- 方案四:SET的扩展命令(SET EX PX NX)
- 方案五:SET EX PX NX + 校验唯一随机值,再释放锁
- 方案六: 开源框架~Redisson
- 方案七:多机实现的分布式锁Redlock
什么是分布式锁
- 「互斥性」: 任意时刻,只有一个客户端能持有锁.
- 「锁超时释放」:持有锁超时,可以释放,防止不必要的资源浪费,也可以防止死锁.
- 「可重入性」:一个线程如果获取了锁之后,可以再次对其请求加锁.
- 「高性能和高可用」:加锁和解锁需要开销尽可能低,同时要保证高可用,避免分布式锁失效.
- 「安全性」:锁只能被持有的客户端删除,不能被其他客户端删除
Redis分布式锁方案一:SETNX + EXPIRE
if(jedis.setnx(key_resource_id,lock_value) == 1){ //加锁 expire(key_resource_id,100); //设置过期时间 try { do something //业务请求 }catch(){ } finally { jedis.del(key_resource_id); //释放锁 } }
Redis分布式锁方案二:SETNX + 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) < System.currentTimeMillis()) { // 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间(不了解redis的getSet命令的小伙伴,可以去官网看下哈) String oldValueStr = jedis.getSet(key_resource_id, expiresStr); if (oldValueStr != null && oldValueStr.equals(currentValueStr)) { // 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才可以加锁 return true; } } //其他情况,均返回加锁失败 return false; }
- 过期时间是客户端自己生成的(System.currentTimeMillis()是当前系统的时间),必须要求分布式环境下,每个客户端的时间必须同步.
- 如果锁过期的时候,并发多个客户端同时请求过来,都执行jedis.getSet(),最终只能有一个客户端加锁成功,但是该客户端锁的过期时间,可能被别的客户端覆盖
- 该锁没有保存持有者的唯一标识,可能被别的客户端释放/解锁.
Redis分布式锁方案三:使用Lua脚本(包含SETNX + EXPIRE两条指令)
if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then redis.call('expire',KEYS[1],ARGV[2]) else return 0 end;
String lua_scripts = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then" + " redis.call('expire',KEYS[1],ARGV[2]) return 1 else return 0 end"; Object result = jedis.eval(lua_scripts, Collections.singletonList(key_resource_id), Collections.singletonList(values)); //判断是否成功 return result.equals(1L);
Redis分布式锁方案方案四:SET的扩展命令(SET EX PX NX)
- NX :表示key不存在的时候,才能set成功,也即保证只有第一个客户端请求才能获得锁,而其他客户端请求只能等其释放锁,才能获取.
- EX seconds :设定key的过期时间,时间单位是秒.
- PX milliseconds: 设定key的过期时间,单位为毫秒
- XX: 仅当key存在时设置值
if(jedis.set(key_resource_id, lock_value, "NX", "EX", 100s) == 1){ //加锁 try { do something //业务处理 }catch(){ } finally { jedis.del(key_resource_id); //释放锁 } }
- 问题一:「锁过期释放了,业务还没执行完」.假设线程a获取锁成功,一直在执行临界区的代码.但是100s过去后,它还没执行完.但是,这时候锁已经过期了,此时线程b又请求过来.显然线程b就可以获得锁成功,也开始执行临界区的代码.那么问题就来了,临界区的业务代码都不是严格串行执行的啦.
- 问题二:「锁被别的线程误删」.假设线程a执行完后,去释放锁.但是它不知道当前的锁可能是线程b持有的(线程a去释放锁时,有可能过期时间已经到了,此时线程b进来占有了锁).那线程a就把线程b的锁释放掉了,但是线程b临界区业务代码可能都还没执行完呢.
方案五:SET EX PX NX + 校验唯一随机值,再删除
if(jedis.set(key_resource_id, uni_request_id, "NX", "EX", 100s) == 1){ //加锁 try { do something //业务处理 }catch(){ } finally { //判断是不是当前线程加的锁,是才释放 if (uni_request_id.equals(jedis.get(key_resource_id))) { jedis.del(lockKey); //释放锁 } } }
if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end;
Redis分布式锁方案六:Redisson框架
Redis分布式锁方案七:多机实现的分布式锁Redlock+Redisson
- 按顺序向5个master节点请求加锁
- 根据设置的超时时间来判断,是不是要跳过该master节点.
- 如果大于等于3个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦.
- 如果获取锁失败,解锁!
边栏推荐
- How ArkUI adapter somehow the screen
- Big guys.Use flink-cdc-sqlserver version 2.2.0 to read sqlserver2008R
- yolov5s用自己的数据集进行训练模型
- C专家编程 第1章 C:穿越时空的迷雾 1.9 阅读ANSI C标准,寻找乐趣和裨益
- TCP 可靠吗?为什么?
- 虹科分享 | 如何测试与验证复杂的FPGA设计(3)——硬件测试
- 面了个腾讯35k出来的,他让我见识到什么叫精通MySQL调优
- DAYU200 OpenHarmony标准系统HDMI全屏显示
- 大佬们。使用flink-cdc-sqlserver 2.2.0 版本读取sqlserver2008R
- Windows 事件查看器记录到 MYSQL
猜你喜欢
组件通信-父传子组件通信
Understand the recommendation system in one article: Outline 02: The link of the recommendation system, from recalling rough sorting, to fine sorting, to rearranging, and finally showing the recommend
面试不再被吊打!这才是Redis分布式锁的七种方案的正确打开方式
详谈RDMA技术原理和三种实现方式
Component communication - parent-child component communication
leetcode:202. 快乐数
EasyExcel实现动态列解析和存表
建造者模式/生成器模式
高效的组织信息共享知识库是一种宝贵的资源
MATLAB | 七夕节快到了,还不给朋友安排上这个咕呱小青蛙?
随机推荐
SwinIR实战:详细记录SwinIR的训练过程
SwinIR实战:如何使用SwinIR和预训练模型实现图片的超分
C专家编程 第3章 分析C语言的声明 3.1 只有编译器才会喜欢的语法
数据中台“集存通用治”功能场景说明
生产环境如何删除表呢?只能在SQL脚本里执行 drop table 吗
机器人开发--Universal Scene Description(USD)
【翻译】关于扩容一个百万级别用户系统的六个课程
【带你了解SDN和网络虚拟化】
如何设计大电流九线导电滑环
[Unity Getting Started Plan] Basic Concepts (7) - Input Manager & Input Class
【无标题】
leetcode:202. 快乐数
Windows 事件转发到 SQL 数据库
最强分布式锁工具:Redisson
DataGrip数据仓库工具
node connection mongoose database process
WordPress建站技术笔记
C专家编程 第1章 C:穿越时空的迷雾 1.10 “安静的改变”究竟有多少安静
[Unity Getting Started Plan] Basic Concepts (8) - Tile Map TileMap 02
C专家编程 第3章 分析C语言的声明 3.9 轻松一下---驱动物理实体的软件