孤尽班第十二次课-Redis分布式锁
分布式锁简介
在同一个JVM 内部,大家往往采用synchronized 或者Lock 的方式来解决多线程间的安全问题,但是在分布式架构下,在JVM 之间,那么就需要一种更佳高校的锁机制,来处理这种跨JVM 进程之间的线程安全问题,解决方案就是: 使用分布式锁。
Redis 原理解析
Redis 分布式锁原理
Redis 分布式锁机制,主要借助setnx 和 expire 两个命令完成。
1. setnx 当key 不存在,将key 设置为value, 存在不做任何操作,返回0.
2. expire 设置key 过期时间。
原理解析:
3. key 不存在时创建,并设置value 和过期时间,返回值为1, 成功获取到锁。
如key 存在时直接返回0,抢锁失败。
持有锁的线程释放锁时,手动删除key, 或者过期时间到,key 自动删除,锁释放。
set key value[Ex seconds] [PX millisecomds] [NX|XX]
EX second: 设置失效时长, 单位秒
milliseco 失效 nds: 设置时长,单位毫秒
key 不存在时设置value, 成功返回OK ,失败返回nil
XX key 存在时设置valuem, 成功 返回OK ,失败返回nil
Redis 中使用Lua
EVAL: 对Lua 脚本进行求值,命令如下:
EVAL script numkeys key [key.......] arg [arg....]
1. script : 参数是一段Lua.5.1 脚本程序 。它会被运行在Redis 服务器上下文中。
2. numkeys: 参数用于指定键名参数的个数。
# 在Lua 脚本中可以使用redis.call() 函数来执行Redis 命令
## 这段脚本的确实现了将键foo 的值设为bar
eval "return redis.call('set',KEYS[1],ARGV[1])" I foo bar
> OK
Jedis 分布式锁实现
加锁: 就是调用SET key PX NX 命令
set key value [EX seconds] [PX millseconds] [NX|NX]
key 加锁的key
value UUID.randomUUID().tostring(). 代表加锁的客户端请求标识。
nxxx nx 表示SET IF NOT EXIS 可以使用T
expx PX 表示毫秒
time: 表示过期时间
锁过期问题
预估业务操作10 秒, 锁设置20秒。 各种原因,比如STW 业务操作执行超过20秒 了, 业务会在无锁状态下运行,就会发生数据紊乱。
注: STW : java 中 Stop - The- World 机制简称STW, 常发生于fullGC 这时Java 应用程序的其他所有线程都被挂起(除了垃圾收集器之外)
- 乐观锁方式 增减版本号。
- watch dog 自动延期
锁过期解决方案:
乐观锁: 增加版本号需要调整业务逻辑。与之配合,所以会入侵代码。
Watch dog 自动延期机制
不会侵入业务代码,redisson就是 采用这种解决方案。
客户端1 加锁的key 默认生存时间才30秒, 如果超过了30 秒, 客户端1 还想一直持有这把锁,怎么办呢? 只要客户端1 一旦加锁成功,就会启动一个watch dog 看门狗,他是后台一个线程,会每隔10 秒检查一下,如果客户端1 还持有锁key, 那么就会不断的延长锁key 的生存时间。
Redisson 分布式锁
- Redisson 简介
Redisson 是 基于 Netty的Redis 客户端。不但能操作原生的Redis 数据结构,还为使用者提供了一系列具有分布式特性的常用工具类,实现了分布式锁。
Redisson 分布式锁
Redis 分布锁 和 JUC 的Lock 方法相似。RLock 接口继承了Lock 接口, 代码如下。
锁的存储结构:
锁的结构式Hash
key: 锁的名字
字段: UUID+ threadid
值: 表示重入的次数
Redisson 分布锁原理
**加索**
1. 判断有没有 “DISLOCK”
2. 如果没有,设置UUID:1 = 1
3. 设置它的过期时间
锁重入
4. key 和字段都存在,锁重入
5. 执行命令incrby UUID:1 1
6. 结果:DISLOCK: {UUID: 1 2}
锁互斥
1 .客户端2 进入
7. 判断有KEY,没有字段
8. 返回过期时间
9. 客户端2 自旋等待
释放锁
1. 判断KEY 是否存在
2. 如果不存在,返回nil
3. 如果存在,使用hincryby -1 减 1
4. 减完后m, count > 0 值 仍大于0 则返回0
5. 减完后 ,count <= 0 ,则删除key
6. 用publish 广播锁释放消息
Watch dog 自动延期
watch dog 当加锁成功后,同时开启守护线程,默认有效期是30 秒,每隔10秒就会给锁延续期到30秒
- watch Dog 只有 在未显示指定加锁时间才会生效
- lockWatchdog Timeout 可以设置超时时间
分段锁
这是雪松老师的思想设计
可以使用分段的方式(思想来源) map / reduce , ConcurrentHashMap ),以空间换时间。为了达到每秒600 个 订单, 可以将锁 分成 600/ 5 = 120 个 段 反过来, 每个段1 秒可以操作5 次, 120 个段, 合起来, 每秒操作600 次。