Redis锁的使用姿势

单机设置锁

redis提供了SET NX命令来设置不存在的key值,以及SET PX设置过期时间。我们可以结合这两个命令在redis做到加锁操作

锁的值最好设置为唯一的随机值,这是为了方便之后以安全的方式去释放锁

# 设置不存在的lock_test键的值为1,且过期时间为5000ms
SET lock_test 1 NX PX 5000

以下是删除特定value的锁的lua脚本,这样就可以防止删除不属于自己的锁

lua脚本是原子运行,因此并不会出现获取是目标值,删除的时候变成了其他值

if redis.call("get", KEYS[1]) == ARGV(1) then
  return redis.call("del", KEYS[1])
else 
  return 0
end  

超时同时持有锁

在上述锁中,存在一个问题——如果A获取了锁,但是由于执行时间过长,导致B也获取到过期后的锁,此时并会同时存在多方获取锁

为了解决该问题,有以下解决方案

  • 将过期时间设置的足够长。

    若获取到锁的任务被中止后,下一个任务需要等待较长时间才能重新获取

  • 续过期时间

    对业务有一定侵入

无法堵塞等待锁释放

由于上述命令都是立刻返回的,所以无法进行堵塞获取锁,因此

  • 进行轮询

    在锁竞争比较激烈情况下,性能损耗较大

  • 采用redis发布订阅,接受锁释放成功消息

    不存在ack和持久化保存机制,这可能导致出现意外的情况下,比如redis宕机、网络异常导致已经解锁的消息无法成功发送给订阅者,导致永远也无法解锁

  • redis stream

RedLock

考虑到redis主从同步集群中,如果从master获取到锁之后,就故障换成了从节点,那么就会导致锁失效,因此提出了RedLock的分布式锁算法

假设有N=5个Redis master节点,保证相互独立

  1. 获取当前时间(以毫秒为单位)
  2. 按照顺序向5个master节点请求加锁。
    1. 设置客户端连接和响应超时时间(超时时间要小于锁失效时间)
    2. 如果请求超时,则立刻尝试下一个master节点
  3. 超过一半master节点都获取到锁,且锁的使用时间小于锁失效时间,认为锁获取成功
  4. 若获取锁失败,则要在所有master节点上解锁

该算法依赖于这样的假设:虽然进程之间没有同步时钟,但每个进程中的本地时间几乎以相同的速率更新,与锁的自动释放时间相比,误差很小

那如果发生了过期,导致锁同时被多任务占有,该如何解决呢?

快过期时可以更新集群锁过期时间,比如可以用以下lua去续锁

if redis.call("GET", KEYS[1]) == ARGV[1] then
		return redis.call("PEXPIRE", KEYS[1], ARGV[2])
	else
		return 0
	end

思考——使用RedLock是个好主意吗?

Martin Kleppmann提出分布式锁的主要作用在于

  • 效率:节省不必要的相同工作
  • 正确性:保证由于并发导致的不正确状态、数据丢失、业务错误等

而Redlock从效率来说不如单redis节点锁,从正确性来说又比不上像zookeeper这样的共识系统

从正确性来分析说,redlock假定了以下条件

  • 所有redis节点时间一致
  • 网络延迟相对于过期时间较小
  • 有限的进程暂停

这可能会存在以下情况

  1. client 1请求节点A、B、C锁,而D、E由于网络原因无法到达
  2. 节点C时钟调快,导致锁过期(或者C丢失锁信息)
  3. client 2请求节点C、D、E锁,而A、B由于网络原因无法到达
  4. client 1、client 2都相信已经持有锁

或者

  1. client 1请求A、B、C、D、E节点锁
  2. client 1发生较长时间的gc
  3. 锁在所有节点过期
  4. client 2获取到所有锁
  5. client 1完成gc后认为已经持有锁,但client 2此时也认为持有锁

antirez对此反驳道redlock记录了获取锁的时间差,并会再次检查是否超时

并且此步骤也考虑到网络延迟问题

以上两种情况都说明了redlock在这种情况下的不可靠性

Ref

  1. https://redis.io/docs/manual/patterns/distributed-locks/
  2. https://xiaomi-info.github.io/2019/12/17/redis-distributed-lock/
  3. https://github.com/go-redsync/redsync
  4. https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html
  5. http://antirez.com/news/101

你可能感兴趣的:(db,distributed,concurrent,redis,数据库)