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