redis分布式锁
背景
单机情况下,对于多线程情况下的竞态资源,我们可以在代码中使用synchronized或者ReentrantLock来加锁,防止发生并发问题。但是当我们的服务进行集群部署时,对于这两种加锁方式,就会失效,它们只能在单机加锁,所以就需要与之对应的分布式锁。
要求
- 互斥:任何时刻只能有一个客户端获取锁。
- 释放死锁:不会发生死锁,即获取锁的服务崩溃,也能释放锁。
- 容错性:(redlock中用)只要多数redis节点(一半以上)在使用,client就可以获取和释放锁。
分布式锁
一般采用redis的setnx原子操作来实现分布式锁。
学习一下
学习二下
学习三下
学习四下
setnx(获得锁)
setnx 是SET if Not eXists(如果不存在,则 SET)的简写。
完整语法:
1 | # set命令模式 |
- value的值尽可能使用随机数或者线程独有的,能够识别的,为了安全的释放锁。
- 使用不同的redis客户端(jedis,redisTemplate)时写法会有所不同,这里是redis黑窗口命令。
参数说明:
- EX:设置过期时间,时间精确到秒
- PX:设置过期时间,时间精确到毫秒
- NX:表示key不存在时才设置,否则返回null
- XX:表示key存在时才设置,否则返回null
使用过程:
- 执行setnx命令进行加锁,返回ok,返回nil则为加锁失败。
- 执行expire命令设置超时时间
- 执行业务逻辑
- delete命令解锁
问题
- 加锁与设置超时时间分步执行,若超时时间设置失败则有可能产生死锁。
- delete命令存在误删非当前线程持有锁的可能。
- 不支持阻塞等待,不可重入。
- 单机redis锁,存在加锁后,主从切换时锁还未同步到问题,锁会丢失。
lua脚本(释放锁)
我们在手动解锁时,极限情况下会有删除其他线程锁的情况,因为我们的随机数比较和删除过程并不是原子操作。存在判断通过后,锁自动失效,其他线程加锁成功的情况,这是解锁会出问题。通过lua脚本原子操作,可以安全的解锁。
1 | -- lua删除锁: |
setex&psetex
setex等同于set命令在可选参数使用EX的情况,都是在NX模式下,添加了过期时间,避免死锁。psetex相对于setex采用毫秒作为超时单位。
相关文章