基本概念
-
什么是分布式锁
- 如果需要控制分布式系统,不能是某个客户端本地的锁
- 锁是保存在一个共享存储系统中的,可以被多个客户端共享访问和获取\
-
为什么选择redis
-
可以被多个客户端共享访问,正好就是一个共享存储系统\
-
读写性能高,可以应对高并发的锁操作场景
-
单机上的锁和分布式锁的联系与区别
-
对于单机锁
- 变量为0,表示线程未获取到锁
- 变量为1,表示已经有线程获取到锁
- 获取锁则把变量置为1
- 释放锁则把变量置为0
-
分布式锁
-
也是通过判断锁的变量值,多个客户端存储数据
-
新的要求
-
分布式锁的加锁和释放锁的过程,涉及多个操作(需要保证原子性)\
-
怎么保证分布式锁的稳定性和可靠性
-
-
基于单个 Redis 节点实现高可靠的分布式锁
-
设置设计分布式锁
- key锁的名称 value锁的状态
-
怎么保证读取锁,判断锁和设置锁操作的原子性
-
使用redis原子命令
- SETNX 如果存在就设置,如果不存在就不设置+DEL删除释放锁\
-
使用Lua脚本
-
-
使用SETNX的潜在风险
- DEL操作异常,导致锁一直不释放 通过设置过期时间避免死锁
- 不是申请的锁的客户端执行DEL会误删除锁 可以将value设为客户端唯一值
- SET 命令的 NX 和 EX/PX 选项\
-
DEL删除释放锁\
- 最好使用lua脚本,保证指令的原子性
- 读取数据,删除释放数据
\
基于多个 Redis 节点实现高可靠的分布式锁
-
Redis 的开发者 Antirez 提出了分布式锁算法 Redlock\
-
如果客户端能够和半数以上的实例成功地完成加锁操作\
- 获取锁成功
- 否则获取锁失败
-
具体实现步骤
-
客户端获取当前时间
-
客户端按顺序依次向 N 个 Redis 实例执行加锁操作(使用指令SETNX value是客户端标识) 顺序加锁\
-
完成加锁,计算总耗时 判断是否成功
- 超过半数加锁成功
- 总耗时未超过过期时间
-
-
可以通过redlock保证可靠性
总结
-
加锁注意点
-
加锁包括了读取锁变量、检查锁变量值和设置锁变量值三个操作 使用SETNX指令\
-
锁变量需要设置过期时间,避免死锁\
-
锁变量的值需要能区分来自不同客户端的加锁操作,以免在释放锁时,出现误释放操作\
-
-
释放锁注意点
-
释放锁也包含了读取锁变量值、判断锁变量值和删除锁变量三个操作\
-
没有指令能够完成
-
一般使用lua脚本实现操作的原子性
-
-
保证稳定性
- 可以使用Redlock算法,实现基于多个实例的分布式锁