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

Redis 分布式锁

时间:2023-06-22 18:12:27浏览次数:42  
标签:加锁 代码 Redis 线程 失效 执行 lockKey 分布式

为什么使用分布式锁

场景

  在分布式系统中,java 中 synchronized 锁只是 JVM 级别的,也就是进程级别。因此,当同一个服务,启动多次出现多个节点时,在不同进程中,相同的同步代码块使用 synchronized,并不能达到想要的同步效果,也就是这个关键字管不到别的进程。

  此时,前端如果出现高并发场景,系统通过负载均衡,将同一个接口的请求,分发到不同节点中处理业务,这些请求都分别能拿到各自所在进程的锁,去处理业务,从而导致问题出现。

解决方案

  在需要同步的代码块前面加 redis 分布式锁,同步代码块执行完毕后,释放锁。从而达到同步操作的目的。

具体解决方案

Reids 命令参考地址

认识 redis 分布式锁实现的关键命令

SET key value [EX seconds] [PX milliseconds] [NX|XX]

参数解释:

EX seconds : 将键的过期时间设置为 seconds 秒。
PX milliseconds : 将键的过期时间设置为 milliseconds 毫秒。PSETEX key milliseconds value 。
NX : 只在键不存在时, 才对键进行设置操作。 执行 SET key value NX 的效果等同于执行 SETNX key value 。
XX : 只在键已经存在时, 才对键进行设置操作。

注意:

  众所周知,中括号参数是可选的,而一个中括号中出现管道符 “|” 时,表示符号两边的参数只能选一个。

 

实际应用

  在 java 中对应的 redis api 如下,使用此接口设置的 key 不能重复。也就是同时多个线程 set 同一个 key 时,只有第一个线程能成功,接口返回 true,后续的线程都将返回 false。

boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, value, timeout, timeUnit);

  所以,通过上面接口就能实现同步功能中的加锁操作,只要线程执行这段代码返回的是 false,就直接 return,后续具体业务逻辑代码不允许执行。

  而解锁方式,就是直接删除这个作为锁使用的 key

redisTemplate.delete(lockKey)

大致代码如下:

// 加锁
boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, value, timeout, timeUnit); if (!result) { log.warn("已经有线程在执行此段代码,其他线程不允许调用"); return; } // 执行业务代码 ... ... 业务代码 ...
// 业务代码执行完毕,删除锁
redisTemplate.delete(lockKey)

 

上面这种简单加锁可能出现的问题

锁失效引起的问题

  高并发情况下,假设上面加锁代码中 lockKey 这个变量值为 "lock",timeout 值为 10,timeUtil 为 TimeUnit.SECONDS 也就是秒。此时线程 A 执行加锁操作后,再执行完同步块中的业务代码需要花 15 秒钟。在线程 A 还未执行完毕,执行到第 10 秒的时候,锁失效时间到了,被 redis 清除了。

  然后线程 B 来了,执行加锁接口返回 true,也就是因为线程 A 锁失效了,所以线程 B 又能成功加锁了。

  线程 B 执行业务代码需要 10 秒,在线程 B 执行到第 5 秒时,线程 A 因为之前已经执行 10 秒,此时又经过 5 秒后,业务逻辑代码执行完毕,执行到删除锁的代码,因为大家使用的是值相同的 lockKey,所以线程 A 成功的删除了线程 B添加的锁。

  这时,线程 C 也来了,发现没锁,它也加锁,然后执行业务代码,它的锁又被 B 清除了......,以此类推,会出现锁永远失效的情况。

锁失效引起的问题 解决方案

  解决执行业务逻辑时,线程加的锁因到了失效时间,被 redis 清除的问题:在执行业务代码前,开个线程,使用定时器,定时更新 lockKey 失效时间。例如上面设置10 秒失效,定时器这边取失效时间的 1/3 (Redisson 也是取1/3时间),作为定时去更新失效时间的时间间隔,只要判断这个 lockKey 还存在,就去更新,避免在线程还未执行完业务代码,锁就是失效了。

  解决锁被其他线程删除的问题:lockKey 使用不同的值,没个线程都各自使用一个唯一的值(如:UUID)作为线程唯一标识。在删除 lockKey 的时候,使用当前前程唯一标识 redis 中缓存的 lockKey 做对比,只有值相同的情况下,才允许删除。

须知:

  上面解决方案实现起来,代码不少,所以可以使用 Redisson 来实现加锁,解锁的操作。Redisson 内部就使用了上面的解决方案,来避免锁失效引起的问题。

使用 Redision 解决大致代码

// 引入 Redisson
@Autowrite
Redisson redisson;

************************

// 使用 Redisson
String lockKey = "lock";
RLock redissonLock = redisson.getLock(lockKey)

// 加锁操作。只有无锁的线程能执行下面代码加锁,其他线程都会被阻塞,所以不需要前面的 if 判断来处理
redissonLock.lock(30, TimeUnit.SECONDS);

// 执行业务代码
...
... 业务代码
...
// 业务代码执行完毕,释放锁(一定要在 finally 中执行)
redissonLock.unlock();

 

主节点宕机,锁没来得及向从节点同步问题

  基于上面 redisson 的加锁场景,在 redis 集群中,如果刚加的锁,主节点还未来的及向从节点同步,就挂了。此时,从节点在选举出新的主节点后,里面并没有刚刚加的锁,这个问题该如何解决。?

 

分布式锁优化问题

  基于上面 redisson 的场景,加的锁该如何优化,提升效率?

 

标签:加锁,代码,Redis,线程,失效,执行,lockKey,分布式
From: https://www.cnblogs.com/zhousjcn/p/17498065.html

相关文章

  • django中使用redis
    django中使用redis方法1,通用安装redis#pipinstallredis#1写一个连接池 importredis.ConnectionPool(host='xx.xx.xx.xx',port=6379,password='xxx',max_connections=1000)#2在使用地方导入即可 conn=redis.Redis(connection_pool=pool)conn.incr(�......
  • redis之列表和其他操作
    redis之列表1lpush(name,values)2rpush(name,values)表示从右向左操作3lpushx(name,value)4rpushx(name,value)表示从右向左操作5llen(name)6linsert(name,where,refvalue,value))7r.lset(name,index,value)8r.lrem(name,value,num)9lpop(name)10......
  • redis使用事务
    redis管道#事务--》四大特性-原子性-一致性-隔离性-持久性#redis使用事务importrediscon=redis.Redis()p=con.pipeline(transacction=True)#使用事务p.multi()开启事务#任务p.execute()p.close()......
  • redis数据类型和字符串操作
    redis数据类型1字符串===》做缓存,做计数器''' setkeyvalue getkey msetk1v1k2v2... mgetk1k2... setexkeyexpvalue incrbykeyincrement'''2列表===》消息队列''' rpushkeyvalue1value2... lpushkeyvalue1valu......
  • redis简单介绍和使用
    redis安装"""1、官网下载:安装包或是绿色面安装2、安装并配置环境变量#官网:https://redis.io/ -下载完是源代码:c语言源码:https://redis.io/download/#redis-stack-downloads-最稳定:6.x-最新7.x#中文网:http://redis.cn/download.html -上面最新只到5.x......
  • hiredis的同步模式和异步模式
    1.什么是hiredisHiredis是一个C语言编写的Redis客户端库,用于与Redis数据库进行交互。它提供了一个简洁而高效的接口,使开发人员可以方便地在自己的C/C++项目中使用Redis。Hiredis是一个开源项目,可从其官方GitHub仓库获取源代码,并在符合BSD许可证的条件下使用和分......
  • REDIS 不单纯的数据库结构
    数据库是一个处理数据的地方,包含了数据计算,数据存储,和数据转换等等,其中处理数据中,数据库结构和数据库库结构的种类,可以作为评判一个数据库某项指标好坏的关键。什么是一个好的数据结构,简单的说,方便数据访问,和管理的数据结构就是一个好的数据结构。REDIS本身虽然是一个缓存式数据库......
  • Redis
    一.设置密码步骤1:application.xml下配置redis 步骤2:redis目录下配置文件redis-windows.conf的requirepass字段requirepasspwd步骤3:redis-cli客户端设置密码configsetrequirepass'pwd'......
  • 什么是 Redis?【Redis】
    Redis是一个基于内存的key-value结构的数据库 主要特点1.基于内存存储,读写性能高-Redis读的速度是110000次/S2.适合存储热点数据(商品、新闻资讯)3.它存储的value类型比较丰富,也称为结构化NoSQL数据库......
  • Redis6 的安装
    安装网址‍Redis官方网站Redis中文官方网站http://redis.iohttp://redis.cn/‍安装版本‍6.2.1forLinux(redis-6.2.1.tar.gz)‍用考虑在windows环境下对Redis的支持‍安装步骤‍①准备工作:下载安装最新版的gcc编译器安装C语言的编译环境:yum......