Redis 分布式锁详解

一、什么是分布式锁不同的进程需要以互斥的方式来访问共享资源 , 这里实现互斥就是分布式锁 。
简单来说就是:同一时间只有一个客户端对共享资源操作 。举个实际例子 , 抢购茅台 , 如果不加锁就会发生超卖的事故 。

Redis 分布式锁详解

文章插图
 
二、实现分布式锁需要注意的点
  1. 互斥性:在任何时刻 , 只有一个客户端获得锁 。
  2. 无死锁:任何时候都能获取锁 , 即使客户端崩溃或者或被分区 。
  3. 正确性:解铃还须系铃人 , 客户端 A 加的锁只能由客户端 A 解锁 , 其他客户端不能解锁 。
  4. 容错:只要大部分 redis 节点处于运行状态 , 客户端就能够获取和释放锁 。
三、Redis 分布式锁原理Redis 加锁主要是使用 set (
https://redis.io/commands/set ) 命令操作:
SET key value [EX seconds|PX milliseconds|KEEPTTL][NX|XX] [GET]
  • EX -- 设置指定的过期时间 , 以秒为单位 。
  • PX -- 设置指定的过期时间 , 以毫秒为单位 。
  • NX -- 仅当该键不存在的时才会设置该键 。
  • XX -- 仅当该键存在时才会设置该键 。
加锁命令: SET lock_key lock_value PX 10000 NX
只有当 lock_key 不存在时才会设置 lock_key 和 lock_value , 超时时间 10000 毫秒 , 设置成功返回 OK:
Redis 分布式锁详解

文章插图
 
当 lock_key 存在时返回 nil:
Redis 分布式锁详解

文章插图
 
Redis 释放锁使用命令: DEL key (
https://redis.io/commands/del )
解锁命令: DEL lock_key  。
Redis 在 2.6.12 之后的版本才加入 [EX seconds|PX milliseconds] [NX|XX] 这些参数
Redis 分布式锁详解

文章插图
 
在此之前使用 SETNX (
https://redis.io/commands/setnx ) SETNX 是 “ SET if N ot e X ists” 的缩写 。
SETNX 返回 1 说明设置成功 ,  返回 0 说明设置失败 。
SETNX 和 EXPIRE 操作之间不是原子性的 , 如果 SETNX 执行成功之后 ,  没有执行 EXPIRE 命令 , 就可能会发生死锁 。
Redis 官网声明 SETNX 在将来的版本中可能会被弃用 , 因为 SETNX 实现的功能 set 都能实现 。
Redis 分布式锁详解

文章插图
 
四、Redis 实现分布式锁注意的点及解决方案
  1. 防死锁
设置锁和设置锁的超时时间要保持原子性 , 这点很容易做到 使用 SET lock_key lock_value PX 10000 NX 命令即可 ,  不要使用 SETNX lock_key lock_value  ,  EXPIRE lock_key 10 这些命令 , 因为他们之间不是原子性的 , 有发生死锁的风险 。
  1. 合理设置锁超时时间
锁的超时时间要大于程序执行的时间 , 否则多个客户端可能同时获取锁 。充分预估使用锁的业务代码执行时间 , 该时间不宜过长也不宜过短 , 过短 , 可能使锁发生错误;过长 , 客户端异常时可能会影响执行效率 。
  1. 释放锁要及时
客户端使用完共享资源之后要及时的释放锁 , 即使在程序发生异常 , JAVA 中一般都是在 finally 里释放锁 。
  1. 只能释放自己加的锁
在释放锁的时要确保这个锁是自己的 , 不能将其他锁释放掉 , 这样可能导致多个客户端同时获取锁 。可以通过判断 lock_value 的值是否相等来判断是否是自己加的锁 , lock_value 的值可以使用 UUID 或者任意确定唯一的值 。
  1. 释放锁要保证原子性
客户端在释放锁时分两个步骤 , 一要比较锁的值是否相等 , 二要删除锁( DEL key ) , 这两个步骤要保证原子性 , 否则的话可能导致将其他锁释放掉 , 画个图解释下:


推荐阅读