Redis - 实现分布式锁
分布式锁实现方式
分布式锁实现方式:
- 基于数据库 原子操作 如shedlock组件
- 基于redis 单点的setnx k v nx px , v需要为随机数,删除时使用lua脚本保证原子性,if v==线程设置的值 才 del 防止其他线程错误释放锁
- redis redlock 多节点依次加锁,大多数节点加锁成功就成功
- redission java redis 客户端,有可重入锁,hset k f v, k是分布式资源名,f是(获取了锁的)线程名 v 值,如果是自己获取的锁,可以加一,释放锁就减一,其他线程就获取不到
- zookeeper利用临时顺序节点 实现分布式锁 原理
Redis实现分布式锁
单节点分布式锁
本地的锁操作非常常见,无非就是申请一个锁变量lock, 加锁时,判断锁变量是否被持有,如果被持有中,则加锁失败,如果不被持有,则加锁成功,将锁的状态改为持有锁的这个线程信息。加锁成功,执行完相应操作后,再释放锁,即将锁状态该线程的信息抹掉。
而在分布式系统中,如果需要加锁,那么锁资源必须在一个可以被各个实例访问的共享资源里。Redis就可以作为这个共享资源。在加锁的过程中,需要注意的地方是,
- 加锁,需要判断锁变量的值(也就是本地锁的锁状态),如果可以被加锁,则设置为加锁后的值,这一系列的操作需要是原子的。
- 释放锁,不能由于某个客户端挂了,该锁就一直不能释放,影响其他客户端加锁。
针对以上第一点,单节点的Redis加锁,可以使用setnx
命令,set lock_key unique_value nx px 1000
,即设置lock_key这个键的值,如果该键存在(已经被其他客户端设置,且处于持有中,未释放)就不操作,如果该键不存在,就设置值,该值为此客户端的信息。而且,Redis的单命令操作可以保证原子性。
释放锁的时候,可以用DEL
该键的方法来释放,为了避免出现不同客户端错误释放共享的情况,我们需要比较键的值为此客户端信息时,才释放,Redis也可以用Lua脚本保证操作原子性,如下:
if redis.call("get", lock_key) == unique_value then
return redis.call("del", lock_key)
else
return 0
end
多节点分布式锁
单节点的分布式锁,简单清晰也很方便,缺点就是如果节点挂了,那么所有加锁操作都会失败。所以为了提高分布式锁的高可靠,Redis开发者提供了一个Redlock的锁算法。算法的思路为:客户端与多个实例节点依次加锁,如果能够在半数以上加锁成功且获取锁过程的耗时小于锁的有效时间,那么认为客户端成功地获取了分布式锁,否则即为失败。