一、分布式锁简介
在同一个JVM内部,大家往往采用synchronized或者Lock的方式来解决多线程间的安全问题,但在分布式架构下,JVM之间就需要一种更加高级的锁机制,来处理跨JVM进程之间的线程安全问题,解决方案就是使用分布式锁。
二、Redis分布式锁分析
Redis分布式锁机制,主要借助setnx和expire两个命令完成
原理
key不存在时创建,并设置value和过期时间,返回值为1;成功获取到锁
如key存在时直接返回0,抢锁失败
持有锁的线程释放锁时,手动删除key;或者过期时间到,key自动删除,锁释放
加锁后一直被占用的解决方案
一是Set同时设置过期时间命令
set key value [EX seconds] [PX milliseconds] [NX|XX]
NX为key不存在时设置value,XX为key存在时设置,成功和失败均分别返回OK/(nil)
二是使用lua脚本,将加锁的命令放在lua脚本中原子性的执行
eval script numkeys key [key …] arg [arg …]
在Lua脚本中,可以使用redis.call()函数来执行Redis命令
栗子:eval “return redis.call(‘set’,KEYS[1],ARGV[1])” 1 foo bar → 将键foo的值设为bar
锁过期问题
预估业务操作10秒,锁设置20秒;假如因STW问题,业务操作执行超过20秒,业务在无锁状态下运行,就会发生数据紊乱
解决方案:一是乐观锁方式,增加版本号(需调整业务逻辑);二是watch dog自动延期
watch dog自动延期机制 → 不会侵入业务代码,redisson就是采用这种方案
三、Redisson分布式锁
Redisson是基于Netty的Redis客户端,不但能操作原生的Redis数据结构,还位使用者提供了一系列具有分布式特性的常用工具类,实现了分布式锁
Redisson加锁原理
加锁 1.判断有没有”DISLOCK” 2.如果没有,设置UUID:1=1 3.设置它的过期时间
锁重入 1.KEY和字段都存在,锁重入 2.执行命令incrby UUID:1 1 3.结果:DISLOCK:{UUID:1 2}
锁互斥 1.客户端2进入 2.判断有KEY,没有字段 3.返回过期时间 4.客户端2自旋等待
Redisson释放锁原理
1.判断KEY是否存在 2.如果不存在,返回nil 3.如果存在,使用hincrby-1,减1 4.减完后,counter>0则返回0 5.减完后,counter<0,删除key 6.用publish广播锁释放消息
watch dog自动延期
当加锁成功后,同时开启守护线程,默认有效期30秒,每隔10秒会给锁续期到30秒
watchDog只有在未显示指定加锁时间时才会生效
lockWatchdogTimeout:可以设置超时时间
四、分段锁
分段锁设计
分布式锁原理
初始化分段锁库存
分段锁扣减库存
分段锁实现
对上述实现进行测试
运行结果发现,比普通锁快3-5倍



