More Related Content Similar to redis实现分布式锁.pptx Similar to redis实现分布式锁.pptx (17) redis实现分布式锁.pptx6. • 问题一:
• 假如某个客户端在执行了 SETNX 命令、加锁之后,紧接着却在操作
共享数据时发生了异常,结果一直没有执行最后的 DEL 命令释放锁。
因此,锁就一直被这个客户端持有,其它客户端无法拿到锁,也无法
访问共享数据和执行后续操作,这会给业务应用带来影响。
• 问题二:
• 如果客户端 A 成功获取到了锁,并且设置了过期时间 30 秒,但线程
A 执行时间超过了 30 秒,锁过期自动释放,此时线程 B 获取到了锁;
随后 A 执行完成,线程 A 使用 DEL 命令来释放锁,但此时线程 B 加
的锁还没有执行完成,线程 A 实际释放的线程 B 加的锁。
8. redis SET的扩展命令(SET EX PX NX)
• SET key value [EX seconds | PX
milliseconds] [NX]
• 只有 key 不存在时,SET 才会创建 key,
并对 key 进行赋值。另外,key 的存活时间
由 seconds 或者 milliseconds 选项值来决
定
9. SET EX PX NX + 校验唯一随机值,再删除
• 加锁:SET $lock_key $unique_id EX
$expire_time NX
• 操作共享资源
• 释放锁:Lua 脚本,先 GET 判断锁是否归
属自己,再 DEL 释放锁
16. Redlock算法流程
• 客户端先获取「当前时间戳T1」
• 客户端依次向这 5 个 Redis 实例发起加锁请求(用前面讲到的 SET
命令),且每个请求会设置超时时间(毫秒级,要远小于锁的有效时
间),如果某一个实例加锁失败(包括网络超时、锁被其它人持有等
各种异常情况),就立即向下一个 Redis 实例申请加锁
• 如果客户端从 >=3 个(大多数)以上 Redis 实例加锁成功,则再次
获取「当前时间戳T2」,如果 T2 - T1 < 锁的过期时间,此时,认为
客户端加锁成功,否则认为加锁失败
• 加锁成功,去操作共享资源(例如修改 MySQL 某一行,或发起一个
API 请求)
• 加锁失败,向「全部节点」发起释放锁请求(前面讲到的 Lua 脚本释
放锁)
17. • 1.客户端在多个 Redis 实例上申请加锁。
• 2.必须保证大多数节点加锁成功。
• 3.大多数节点加锁的总耗时,要小于锁设置的过期时间。
• 4.释放锁,要向全部节点发起释放锁请求。
18. 假设一共有5个Redis节点:A, B, C, D, E。设想发生了如下的事件序列:
• 客户端1成功锁住了A, B, C,获取锁成功(但D和E没有锁住)。
• 节点C崩溃重启了,但客户端1在C上加的锁没有持久化下来,丢失了。
• 节点C重启后,客户端2锁住了C, D, E,获取锁成功。
Redlock缺陷
19. Redlock缺陷
• 假设一共有5个Redis节点:A, B, C, D, E。设想发生了如下的事件序列:
• 客户端 1 获取节点 A、B、C 上的锁,但由于网络问题,无法访问 D 和
E节点, C 上的时钟「向前跳跃」,导致锁到期
• 客户端 2 获取节点 C、D、E 上的锁,由于网络问题,无法访问 A 和 B
• 客户端 1 和 2 现在都相信它们持有了锁(冲突)
23. ZooKeeper实现分布式锁
• 临时节点方式
• 1.让多个进程(或线程)竞争性地去创建同一个临时节点,由于
ZooKeeper 不允许存在两个完全相同节点,因此必然只有一个进程能
够抢先创建成功 。
• 2、假设是进程 A 成功创建了节点,则它获得该分布式锁。此时其他
进程需要在 parent_node 上注册监听,监听其下所有子节点的变化,
并挂起当前线程。
• 3、当 parent_node 下有子节点发生变化时候,它会通知所有在其上
注册了监听的进程。这些进程需要判断是否是对应的锁节点上的删除
事件。如果是,则让挂起的线程继续执行,并尝试再次获取锁。
• 4.这种方式是非公平锁,也就是说在进程 A 释放锁后,进程 B,C,
D 发起重试的顺序与其收到通知的时间有关,而与其第一次尝试获取
锁的时间无关,即与等待时间的长短无关。
25. ZooKeeper缺陷
• 客户端 1 创建临时节点 /lock 成功,拿到了锁
• 客户端 1 发生长时间 GC
• 客户端 1 无法给 Zookeeper 发送心跳,Zookeeper 把临时节点「删
除」
• 客户端 2 创建临时节点 /lock 成功,拿到了锁
• 客户端 1 GC 结束,它仍然认为自己持有锁(冲突)
29. 结论
• 加锁包括了读取锁变量、检查锁变量值和设置锁变量值三个操作,但
需要以原子操作的方式完成,所以,我们使用 SET 命令带上 NX 选
项来实现加锁;
• 锁变量需要设置过期时间,以免客户端拿到锁后发生异常,导致锁一
直无法释放,所以,我们在 SET 命令执行时加上 EX/PX 选项,设置
其过期时间;
• 锁变量的值需要能区分来自不同客户端的加锁操作,以免在释放锁时,
出现误释放操作,所以,我们使用 SET 命令设置锁变量值时,每个
客户端设置的值是一个唯一值,用于标识客户端。
• Redlock 的个人看法是,尽量不用它,而且它的性能不如单机版
redis,部署成本也高。
• 相比较redis和ZooKeeper,个人倾向于redis实现。