首页 > 数据库 >Redis实现分布式锁

Redis实现分布式锁

时间:2024-02-21 21:13:08浏览次数:22  
标签:实现 lock redis ARGV KEYS call Redis 分布式

方案1: spring-integration-redis组件

使用spring-integration-redis组件

private static void tryLock() {
    LettuceConnectionFactory connectionFactory = createConnectionFactory();
    RedisLockRegistry lockRegistry = new RedisLockRegistry(connectionFactory, "testKey");
    Lock lockKey = lockRegistry.obtain("lockKey");
    System.out.println(lockKey.tryLock());
  }

使用Redis实现分布式锁的原理具体就是使用Lua脚本来保证多个命令的原子性。

RedisLockRegistry实现原理

  • 如果有多个项目实例,就表示有多个RedisLockRegistry对象在多个JVM中
  • 每一个的clientId都不一致,先锁本地,再锁Redis。
  • 如果不使用本地锁,每次请求都必须有一个clientId。

青铜方案:

缺陷:业务代码出现异常或者服务器宕机,没有执行主动删除锁的逻辑,就造成了死锁。
改进:设置锁的自动过期时间,过一段时间后,自动删除锁,这样其他线程就能获取到锁了。

白银方案:

缺陷:占锁和设置锁过期时间是分步两步执行的,不是原子操作。
改进:占锁和设置锁过期时间保证原子操作。

黄金方案:

缺陷:主动删除锁时,因锁的值都是相同的,将其他客户端占用的锁删除了。
改进:每次占用的锁,随机设为较大的值,主动删除锁时,比较锁的值和自己设置的值是否相等。

铂金方案:

缺陷:获取锁、比较锁的值、删除锁,这三步是非原子性的。中途又可能锁自动过期了,又被其他客户端抢占了锁,导致删锁时把其他客户端占用的锁删了。
改进:使用 Lua 脚本进行获取锁、比较锁、删除锁的原子操作。

钻石方案:

缺陷:非专业的分布式锁方案。
改进:Redission 分布式锁。

方案2: Redission

使用方便,leaseTime参数表示锁的自动释放时间

// 1.设置分布式锁
RLock lock = redisson.getLock("lock");
// 2.占用锁
lock.lock();
// 3.执行业务
...
// 4.释放锁
lock.unlock();

看门狗机制:如果没有设置锁的自动释放时间,每隔10S都会自动续期,这样就避免了业务处理时间过长导致锁被自动释放导致锁被其他线程获取的问题。
如果设置了锁的自动释放时间,那么业务处理时间不能超过这个时间,不然报错,两次释放锁。

if (redis.call('exists', KEYS[1]) == 0) then   //第一次进入,lock不存在,创建
  redis.call('hincrby', KEYS[1], ARGV[2], 1); 
  redis.call('pexpire', KEYS[1], ARGV[1]);
  return nil;
  end;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then  //第二次进入,当前线程已经获取到了lock
  redis.call('hincrby', KEYS[1], ARGV[2], 1); 
  redis.call('pexpire', KEYS[1], ARGV[1]);
  return nil;
  end;
return redis.call('pttl', KEYS[1]);//当前线程不能获取到锁

lua脚本原理解析

KEYS[1]=lock锁名称,如lock名称为test_lock001,类型为hash
ARGV[1]=lock锁的占用时间,毫秒
ARGV[2]=hash的key,实际值为uuid:threadId,如e6818ee9-1155-428d-8032-280b184209f8:17

参考

Spring Boot Redis 实现分布式锁,真香!!
Redis 分布式锁原理看这篇就够了, 循循渐进
Redis 分布式锁|从青铜到钻石的五种演进方案
分布式锁中的王者方案 - Redisson

标签:实现,lock,redis,ARGV,KEYS,call,Redis,分布式
From: https://www.cnblogs.com/strongmore/p/18026218

相关文章

  • redis
    安装环境与工具yum-yinstallwgetvimtclgccmake下载rediswget wgethttps://download.redis.io/releases/redis-7.0.12.tar.gz 上传 redis-7.0.12.tar.gz 到三台服务器 /data/redis-7.0.12.tar.gz 118.89.112.205 主服务器124.220.13.28  从服务器 ......
  • 你对SPA单页面的理解,它的优缺点分别是什么?如何实现SPA应用呢?
    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助一、什么是SPASPA(single-pageapplication),翻译过来就是单页应用SPA是一种网络应用程序或网站的模型,它通过动态重写当前页面来与用户交互,这种方法避免了页面之间切换打断用户体验在单页应用中,所有必要的代码(HTML、Ja......
  • python实现不同电脑之间视频传输功能
    python实现不同电脑之间视频传输功能   这篇文章主要介绍了python实现不同电脑之间视频传输,本文视频传输实现的前提是确保发送端和接收端接在同一个局域网下,分为发送端和接收端,通过实例代码给大家介绍的非常详细,需要的朋友参考下吧 1.imageZMQ库实现imageZMQ库链接:ht......
  • Java导入功能实现
    今天记录一下Java实现导入数据到数据,导入失败显示如下,会告诉你哪里出错了。controller代码实现@Operation(summary="导入工单")@PostMapping("/importOrderData")publicBaseResponse<String>importOrderData(@RequestParam@Parameter(name="excelFile",......
  • SpringBoot整合Quartz实现动态定时任务
    1、增加依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency><!--json工具--><dependency><groupId>com.alibaba</gro......
  • 利用jszip实现批量文件压缩下载
    介绍前端可以通过一个第三方库jszip,可以把多个文件以blob、base64或纯文本等形式,按自定义的文件结构,压缩成一个zip文件,然后通过浏览器download下来。官网:stuk.github.io/jszip/用法不难,直接看code://先封装一个方法,请求返回文件blobasyncfunctionfetchBlob(fetchUrl,meth......
  • 实现两个输入框互动,一个输入框写文字自动出现在第二个输入框里面
    我们可以为第一个输入框添加一个事件监听器来监听输入(比如input事件),每当第一个输入框的值发生变化时,就会将这个值同步到第二个输入框中。第二个输入框不需要添加这样的监听器,因此它的输入不会影响第一个输入框。以下是一个示例HTML结构和使用jQuery实现的脚本:`Input......
  • SpringBoot+MybatisPlus+Mysql实现批量插入万级数据多种方式与耗时对比
    场景若依前后端分离版本地搭建开发环境并运行项目的教程:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108465662若依前后端分离版如何集成的mybatis以及修改集成mybatisplus实现Mybatis增强:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1362030......
  • B站弹幕不遮挡人像实现
    背景在逛b站的时候,发现一些视频的弹幕都在视频后面,不会遮挡人像,这样看起来很舒服。于是就想着,能不能自己实现一下呢?于是就有了这篇博客。探究源码1、通过调试控制台发现每个video标签都覆盖了个弹幕的div,这个div有个mask-image属性。这个属相包含了一张扣出的图片。推测就是通......
  • redis自学(2)IntSet
    IntSetIntSet是redis中set集合的一种实现方式,基于整数数组来实现,并且具备长度可变、有序等特征。  可能会有疑惑,int8_t的数组contents只有1个字节,怎么可能存的下数组,其实这里的contents存储的只是指向真正数组的指针。IntSet的取值范围大小,实际上是由encoding属性决定的......