使用Redis官方推荐Redlock实现分布式锁

前言:

实现分布式锁的几种方案

1.Redis实现 (推荐)

2.Zookeeper实现

3.数据库实现

项目中使用Eureka注册中心,所以未研究Zookeeper实现.

什么是分布式锁

分布式锁的特征:

  • 「互斥性」: 任意时刻,只有一个客户端能持有锁。

  • 「锁超时释放」:持有锁超时,可以释放,防止不必要的资源浪费,也可以防止死锁。

  • 「可重入性」:一个线程如果获取了锁之后,可以再次对其请求加锁。

  • 「高性能和高可用」:加锁和解锁需要开销尽可能低,同时也要保证高可用,避免分布式锁失效。

  • 「安全性」:锁只能被持有的客户端删除,不能被其他客户端删除

数据库实现分布式和redis实现的缺点

数据库实现分布式缺点:
缺点:

1、这把锁强依赖数据库的可用性,数据库是一个单点,一旦数据库挂掉,会导致业务系统不可用。

2、这把锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获得到锁。

3、这把锁只能是非阻塞的,因为数据的insert操作,一旦插入失败就会直接报错。没有获得锁的线程并不会进入排队队列,要想再次获得锁就要再次触发获得锁操作。

4、这把锁是非重入的,同一个线程在没有释放锁之前无法再次获得该锁。因为数据中数据已经存在了。

Redis实现布式锁缺点:
缺点: 使用 Redis 实现分布式锁方案最大的问题就是如果你对某个 Redis Master 实例完成了加锁,此时 Master 会异步复制给其对应的 slave 实例。但是这个过程中一旦 Master 宕机,主备切换,slave 变为了 Master。接着就会导致,客户端 2 来尝试加锁的时候,在新的 Master 上完成了加锁,而客户端 1 也以为自己成功加了锁,此时就会导致多个客户端对一个分布式锁完成了加锁,这时系统在业务语义上一定会出现问题,导致各种脏数据的产生。所以这个就是 Redis Cluster 或者说是 Redis Master-Slave 架构的主从异步复制导致的 Redis 分布式锁的最大缺陷(在 Redis Master 实例宕机的时候,可能导致多个客户端同时完成加锁)。

使用Redis官方推荐的Redlock实现分布式锁

官网文档地址如下:Distributed locks with Redis – Redis

redlock算法是为了解决什么问题呢?

在单redis实例实现分布式锁时,可能会出现线程A设置完锁后,master挂掉,slave提升为master,因为异步复制的特性,线程A设置的锁丢失了,这时候线程B设置锁也能够成功,导致线程A和B同时拥有锁

然后redis作者提出了redlock算法

算法描述

  1. 获得当前时间(ms)

  2. 首先设置一个锁有效时间valid_time,也就是超过这个时间后锁自动释放,使用相同的key和value对所有redis实例进行设置,每次链接redis实例时设置一个小于valid_time的超时时间,比如valid_time时10s,那超时时间可以设置成50ms,如果这个实例不行,那么换下一个设置

  3. 计算获取锁总共占用的时间,再加上时钟偏移,如果这个总时间小于valid_time,并且成功设置锁的实例数>= N/2 + 1,那么加锁成功

  4. 如果加锁成功了,那么这个锁的有效时间就是valid_time - 获取锁占用的时间 - 时钟偏移

  5. 如果加锁失败,解锁所有实例(每个redis实例都运行del key)

单节点下模拟

配置

使用Redis官方推荐Redlock实现分布式锁_第1张图片

依赖 

        
            org.springframework.boot
            spring-boot-starter-data-redis
        

        
            org.redisson
            redisson
            3.7.0
        

相关代码

使用Redis官方推荐Redlock实现分布式锁_第2张图片

使用Redis官方推荐Redlock实现分布式锁_第3张图片

使用Redis官方推荐Redlock实现分布式锁_第4张图片

使用Redis官方推荐Redlock实现分布式锁_第5张图片

使用Redis官方推荐Redlock实现分布式锁_第6张图片

Controller使用类 

使用Redis官方推荐Redlock实现分布式锁_第7张图片

 测试

启动两个userservice服务,分别不同端口号,userservice01中途线程沉睡五秒。02不沉睡。

开启debug模式

先查看redis中的所有key

 断点至

使用Redis官方推荐Redlock实现分布式锁_第8张图片

再次查看redis

 使用Redis官方推荐Redlock实现分布式锁_第9张图片

会在redis多出我们加的锁

使用Redis官方推荐Redlock实现分布式锁_第10张图片

在执行玩业务后,需要手动释放掉锁。

继续执行,再次查看 ,锁会被释放

使用Redis官方推荐Redlock实现分布式锁_第11张图片

同时像gateway发起两次请求,根据负载均衡,会各发一次。发现userservice01会沉睡五秒,02无沉睡。正常结果是01五秒后返回值,02秒返回。

因为有个分布式锁后,01和02会获取同一个lock :"lock:user",所以快速发两次请求,会发现02并不会秒返回,因为被01占用了锁,需要等01释放锁,所以两个服务会同时返回结果.

使用Redis官方推荐Redlock实现分布式锁_第12张图片

02并不会秒返回!

你可能感兴趣的:(SpringCloud,java)