Redis深入学习笔记-02(分布式锁)
作者:互联网
分布式应用进行逻辑处理时经常会遇到并发问题。如下图所示,一个操作要修改用户的状态,修改之前要先读出用户的状态,在内存里进行修改,改完了再存回去。这两个操作同时进行的话,就会出现并发问题,因为读取和保存状态这两个操作不是原子操作(原子操作是指不会被线程调度机制打断的操作。这种操作一旦开始,就会一直运行到结束,中间不会有任何线程切换。)导致最后的结果是错误的。
一般使用setnx(set if not exists)指令,只允许被一个客户端占坑。先来先占,用完了,再调用del 指令释放锁。
但是有个问题,如果逻辑执行到中间出现异常了,可能会导致del指令没有被调用,这样就会陷入死锁,锁永远得不到释放。
于是我们在拿到锁之后,再给锁加上一个过期时间,比如5s,这样即使中间出现异常也可以保证5s之后锁会自动释放。
但是以上逻辑还有问题。如图下图所示,如果在setnx和 expire之间服务器程突然挂掉了,可能是因为机器掉电或者是人为造成的,就会导致expire得不到执行也会造成死锁。
问题的根源其实就是setnx和 expire是两条指令而不是原子指令。如果这两条指令可以一起执行就不会出现问题。
为了解决这种问题,作者加入了set指令的扩展参数。
这样setnx和expire组合在一起成为了原子指令。
set name xiaotu ex 100 nx //设置name=xiaotu ,不存在时设置,失效时常100s
超时问题:
Redis的分布式锁不能解决超时问题,如果在加锁和释放锁之间的逻辑执行得太长,以至于超出了锁的超时限制,就会出现问题。因为这时候第一个线程持有的锁过期了,临界区的逻辑还没有执行完,而同时第二个线程就提前重新持有了这把锁,导致临界区代码不能得到严格串行执行。
可重入性
可重入性是指线程在持有锁的情况下再次请求加锁,如果一个锁支持同一个线程的多次加锁,那么这个锁就是可重入的。比如Java语言里有个ReentrantLock就是可重入锁。Redis分布式锁如果要支持可重入,需要对客户端的set方法进行包装,使用线程的Threadlocal变量存储当前持有锁的计数。
后续再对分布式锁做进一步深入。
标签:02,set,加锁,Redis,指令,线程,expire,setnx,分布式 来源: https://blog.csdn.net/weixin_44196561/article/details/120310464