分布式锁

分布式应用进行逻辑处理时经常会遇到并发问题。
Redis分布式锁使用得非常广泛。

1. 分布式锁奥义

分布式锁的本质上是要在Redis里面占一个坑,当别的进程要来占坑,发现那里已经有一个坑了,就只好放弃,或者稍后再试。
占坑一般使用setnx命令,只允许一个客户端占坑。先来先占,用完了,再调用del命令释放“坑”。

setnxt lock:codehole true
... do something
del lock:codehole

这样会存在一个问题,如果逻辑执行到中间出现异常了,可能会导致del指令没有得到调用,这样就会陷入死锁,锁永远得不到释放。
于是我们再拿到锁之后,再给锁加上一个过期时间,比如5s,这样即使中间出现异常也可以保证5s之后锁会自动释放。

setnx lock:codehole true
expire lock:codehole 5
... do something critical
del lock:codehole

但是上面逻辑还是有问题,如果在setnx和expire之间服务器挂了,就会导致expire得不到执行,也会造成死锁。

这种问题的根源在于setnx和expire是两条指令而不是原子指令。如果这两条指令可以一起执行就不会出现问题。
也许会想到使用Redis事物来解决,这里是不行的,因为expire是依赖于setnx的执行结果的,如果setnx没有抢到锁,那么就不应该执行expire。
在Redis2.8版本,作者加入了set指令的扩展阐述,使得setnx 和expire指令可以一起执行了,彻底解决了分布式锁的乱象。

set lock:codehole true ex 5 nx
do something...
del lock:codehole

2. 超时问题

Redis分布式锁不能解决超时问题,如果在加锁和释放锁之间的逻辑执行的太长,超出了锁的限制,就会出现问题。
有一个稍微安全一点的方案是将set指令的value参数设置为一个随机数。
释放锁时,先匹配随机数是否一致,然后再删除key,这是为了确保当前线程占用的锁不会被其他线程释放,除非这个锁是因为过期而被服务器自动释放的。需要通过Luna脚本来进行处理。
但是这也只是相对安全。真的超时了,当前线程没有执行完,其他线程也会乘虚而入。

3. 可重入

可重入性是指线程再持有锁的情况下再次请求加锁,如果一个锁执行一个线程的多次加锁,这个锁就是可重入的。
Redis分布式锁如果要支持可冲入,需要对客户端的set方法进行包装,使用线程的ThreadLocal变量存储当前持有锁的计数。

  • 参考: 《Redis深度历险 核心原理与应用实践》
posted @   CrazyShanShan  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示