您当前的位置:首页 > 电脑百科 > 数据库 > Redis

手写Redis分布式锁

时间:2020-09-27 09:31:07  来源:  作者:

分布式锁使用场景

现在的系统都是集群部署,每个服务都不是单节点的了。比如库存服务,可能部署到3台机器上分别命名为节点1,节点2,节点3。库存服务需要扣减库存,扣减库存肯定需要锁吧,如果使用Lock或者synchronized,只能锁住自己的节点。而从前台访问是随机路由到这3台节点的。如果线程一进来使节点1上了锁,当线程二进来可能访问到的是节点2,这时节点2还没有上锁,那么库存就会扣减错误。而库存扣减还是一个核心操作,现在居然有Bug,想想就可怕。

这时我们就需要一个全局的锁了。

实现全局的锁不一定是redisMySQL,Zookeeper也可设计为分布式锁。本篇主要讲的是Redis分布式锁的实现方式,其他的实现方式不做讲解。MySQL用作分布式锁在性能上并不好,这里不建议使用。对Zookeeper分布式锁有兴趣的可以看看我写的这篇文章。

手写分布式锁

手写Redis分布式锁

 

Zookeeper锁示意图

手写Redis分布式锁

 

当然市面已经有成熟的框架去实现分布式锁了,不需要你重复造轮子了。

手写Redis分布式锁

 

分布式锁实现

Redis分布式锁底层分析

记得之前面试被问Redis分布式锁的底层原理,我是这么回答的

手写Redis分布式锁

 

Redis分布式锁底层

setnx保证锁的唯一性。过期时间保证锁在异常情况下也能解锁。采用Lua脚本操作Redis,使操作具有原子性。后台进程心跳检测,如果当前时间持有锁并且锁还未失效,延长锁的失效时间。如果当前线程没有获取到锁,会一直自旋,直到获取到锁为止。

手写Redis分布式锁

编写加锁方法

手写Redis分布式锁

 

我们来看看这段代码,redisTemplate.execute参数解释如下

String result = (String) redisTemplate.execute(scriptLock,
        redisTemplate.getStringSerializer(),        redisTemplate.getStringSerializer(),        Collections.singletonList(key),        uuid.toString(),        String.valueOf(timeOut));

scriptLock为执行的Redis命令,里面是Lua脚本

手写Redis分布式锁

 

脚本里面有setnx操作,还设置了超时时间。

两个redisTemplate.getStringSerializer()为key和value序列化工具。

后面3个参数为设置key,设置value,设置超时时间。分别对应Lua脚本中的KEYS[1],ARGV[1],ARGV[2]。

如果setnx操作成功,说明锁创建成功,返回new RedisLock(key, uuid.toString())。

如果失败,则一直循环拿锁,直到成功。

另外,这里的value为随机生成的uuid,这是为什么呢?

因为如果某个客户端获取到了锁,但是阻塞了很长时间才执行完,此时可能已经自动释放锁了,此时可能别的客户端已经获取到了这个锁,要是你这个时候直接删除key的话会有问题,所以得用随机值加上面的Lua脚本来释放锁。

编写释放锁的方法

手写Redis分布式锁

 

执行scriptLock2,Lua脚本如下:

手写Redis分布式锁

 

测试代码

手写Redis分布式锁

 

测试结果

2020-08-29 20:54:43.484  INFO 21880 --- [main] com.lvshen.demo.RedisLockTest            : 获得锁
2020-08-29 20:54:49.532  INFO 21880 --- [main] com.lvshen.demo.RedisLockTest            : 未获得锁

这里没有做可重入功能,所以第二次访问的时候,锁还没有释放,所以未获得锁。

我们画一个流程图,完善下上面的流程

手写Redis分布式锁

 

Redis锁逻辑

有关Redis主从同步问题

在Redis集群中,如果Master节点数据还没同步到Slave节点,Slave节点就挂了,下次Slave节点好了之后,就没有保存锁的数据,从而导致锁失效。那该怎么办?

这个场景是假设有一个Redis Cluster,有5个Redis Master实例。然后执行如下步骤获取一把锁:

  • 获取当前时间戳,单位是毫秒
  • 跟上面类似,轮流尝试在每个Master节点上创建锁,过期时间较短,一般就几十毫秒
  • 尝试在大多数节点上建立一个锁,比如5个节点就要求是3个节点(n / 2 +1)
  • 客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了
  • 要是锁建立失败了,那么就依次删除这个锁
  • 只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁
手写Redis分布式锁

 

当超半数的主从同步成功了,才能判定为上锁成功。

Redis分布式锁缺点

我们来说说Redis分布式锁的缺点:

Redis分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能。

如果是Redis获取锁的那个客户端出Bug了或者挂了,那么只能等待超时时间之后才能释放锁。

Redis主从同步RedLock算法存在缺陷,锁的续命设计也很麻烦。

文中涉及的源码见Github

https://github.com/lvshen9/demo/tree/lvshen-dev/src/main/JAVA/com/lvshen/demo/redis/dislock



Tags:Redis 分布式锁   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
分布式锁使用场景现在的系统都是集群部署,每个服务都不是单节点的了。比如库存服务,可能部署到3台机器上分别命名为节点1,节点2,节点3。库存服务需要扣减库存,扣减库存肯定需要锁...【详细内容】
2020-09-27  Tags: Redis 分布式锁  点击:(140)  评论:(0)  加入收藏
前言:在学习过程中,简单的整理了一些redis跟zookeeper实现分布式锁的区别,有需要改正跟补充的地方,希望各位大佬及时指出Redis实现分布式锁思路基于Redis实现分布式锁(setnx)se...【详细内容】
2019-07-24  Tags: Redis 分布式锁  点击:(255)  评论:(0)  加入收藏
前言提到数据一致性、操作原子性,诸如此类的一些与并发有关的词汇时不知道你第一时间会联想到什么呢?我相信大多数人可能会想到“锁”,为什么是锁呢,这个我不多说,大家心里应该都...【详细内容】
2019-07-23  Tags: Redis 分布式锁  点击:(314)  评论:(0)  加入收藏
▌简易百科推荐
来源: my.oschina.net/xiaomu0082/blog/2990388首先说下问题现象:内网sandbox环境API持续1周出现应用卡死,所有api无响应现象刚开始当测试抱怨环境响应慢的时候 ,我们重启一下应...【详细内容】
2021-12-08  Java识堂    Tags:Redis   点击:(16)  评论:(0)  加入收藏
我不知道为什么你会选择对特定数量的“错误”(或警告)如此具体。听起来您正在寻找将要发布到 Yahoo! 的某些文章的内容。 Insider (N Foos to Blah for the BlahBlah)。那说:...【详细内容】
2021-12-07  富集云科技有限公司    Tags:Redis   点击:(14)  评论:(0)  加入收藏
目录 一、背景 二、步骤 0.理论支持 1、获取数据 2、结果 3、分析数据并评估大小 三、关于repl-backlog-size 一、背景 repl-backlog-size控制这个环形缓冲区. ​ 主从断...【详细内容】
2021-11-05  弈秋的美好生活    Tags:redis   点击:(41)  评论:(0)  加入收藏
Redis 性能测试是通过同时执行多个命令实现的。1,Redis-benchmarkRedis性能命令:redis性能命令格式: redis-benchmark [option] [option value] redis 性能测试工具可选参数如...【详细内容】
2021-11-02  川石信息    Tags:Redis   点击:(41)  评论:(0)  加入收藏
1 概述数据结构和内部编码 无传统关系型数据库的 Table 模型schema 所对应的db仅以编号区分。同一 db 内,key 作为顶层模型,它的值是扁平化的。即 db 就是key的命名空间。 key...【详细内容】
2021-11-01  JavaEdge    Tags:Redis   点击:(28)  评论:(0)  加入收藏
普通java中使用引用Java redis 驱动,即可连接:import redis.clients.jedis.Jedis; public class RedisTestJava { public static void main(String[] args) { //连...【详细内容】
2021-10-13  faesuite    Tags:Redis   点击:(34)  评论:(0)  加入收藏
Redis常用的数据结构有 string list set zset hashstringstring 是 Redis 的基本的数据类型,一个 key 对应一个 value。string 类型是二进制安全的,Redis的string可以包含任...【详细内容】
2021-10-12  语霖    Tags:Redis   点击:(36)  评论:(0)  加入收藏
列表类型可以存储一组按插入顺序排序的字符串,它非常灵活,支持在两端插入、弹出数据,可以充当栈和队列的角色。> LPUSH fruit apple(integer) 1> RPUSH fruit banana(integer)...【详细内容】
2021-09-17  深夜敲代码    Tags:Redis   点击:(54)  评论:(0)  加入收藏
Redis持久化意义 是做灾难恢复,数据恢复,也可以归类到高可用的一个环节里面去,比如你的redis整个挂了,然后redis就不可用了,你要做的事情是让redis变得可用,尽快变得可用 大量的请...【详细内容】
2021-08-12  小李说IT    Tags:Redis   点击:(77)  评论:(0)  加入收藏
当查询Redis中没有的数据时,该查询会下沉到数据库层,同时数据库层也没有该数据,当这种情况大量出现或被恶意攻击时,接口的访问全部透过Redis访问数据库,而数据库中也没有这些数据...【详细内容】
2021-07-30  随便t    Tags:缓存穿透   点击:(90)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条