技术的演变都是为了解决问题的,这也是技术演变吸引人的一点。
Redis分布式锁
分布式锁顾名思义 就是在分布式场景下 多台机器竞争一项资源去加锁
最简单的版本
首先就是最简单的版本,可以通过Redis的setnx命令(set if not exist)
setnx key value
这个命令会使Redis中如果不存在key就会创建值为value的key,存在的话就返回0
过期时间
如果获取锁的机器服务挂了呢?
其他机器:奶奶滴 怎么还不释放锁?
所以 有个兜底策略: 设置过期时间(挂了之后过段时间锁自动过期释放了)
此时可以用一个Redis自带的原子性的操作命令:
setnx key value nx ex 100
时间单位是秒
万一释放错锁了怎么办?
看这么一个场景:
涉及到两个问题:
- 任务还没处理完锁就过期释放了
- 释放掉了别人的锁
对应的解决办法:
- 锁的续约(根据业务加长时间,并且每过一段时间访问锁,如果存在就续约,增加时间)
- 给每个锁区分一下,这是谁加的锁(锁的value可以设置UUID等唯一确定的值)
优化过后的:
lua实现原子性
上面的一步删除的时候会发生什么错误?
当然 这个问题上面已经解决了,就是UUID+线程id作为key,已经不会被其他线程删掉了,但是这么看来这种错误也是挺危险的,作为一个兜底方案,我们可以用lua脚本实现原子性
lua脚本可以这么写
最后的防御
分布式系统最大的敌人可能就是NPC了,在这里它是Network Delay, Process Pause, Clock Drift的首字母缩写。我们先看看具体的NPC问题是什么:
- Network Delay,网络延迟。虽然网络在多数情况下工作的还可以,虽然TCP保证传输顺序和不会丢失,但它无法消除网络延迟问题。
- Process Pause,进程暂停。有很多种原因可以导致进程暂停:比如编程语言中的GC(垃圾回收机制)会暂停所有正在运行的线程;再比如,我们有时会暂停云服务器,从而可以在不重启的情况下将云服务器从一台主机迁移到另一台主机。我们无法确定性预测进程暂停的时长,你以为持续几百毫秒已经很长了,但实际上持续数分钟之久进程暂停并不罕见。
- Clock Drift,时钟漂移。现实生活中我们通常认为时间是平稳流逝,单调递增的,但在计算机中不是。计算机使用时钟硬件计时,通常是石英钟,计时精度有限,同时受机器温度影响。为了在一定程度上同步网络上多个机器之间的时间,通常使用NTP协议将本地设备的时间与专门的时间服务器对齐,这样做的一个直接结果是设备的本地时间可能会突然向前或向后跳跃。
详情这篇文章
NPC三个问题都是客观存在且无法消除的,没有完全安全的分布式锁(所以大家还是要根据业务来)
上面的场景中,主节点挂了咋办?
因为Redis是AP架构的,也就是说不能保证高一致性,主节点里的锁也许并没有同步到从节点里
这里可以用Redis官方的Red Lock 保证当前hash算法选择的节点的主从都有key了才会返回true
这种做法也有点“重”了,有些时候,杀鸡焉用牛刀