Redis深入学习笔记-02(分布式锁)

        分布式应用进行逻辑处理时经常会遇到并发问题。如下图所示,一个操作要修改用户的状态,修改之前要先读出用户的状态,在内存里进行修改,改完了再存回去。这两个操作同时进行的话,就会出现并发问题,因为读取和保存状态这两个操作不是原子操作(原子操作是指不会被线程调度机制打断的操作。这种操作一旦开始,就会一直运行到结束,中间不会有任何线程切换。)导致最后的结果是错误的。

Redis深入学习笔记-02(分布式锁)

        一般使用setnx(set if not exists)指令,只允许被一个客户端占坑。先来先占,用完了,再调用del 指令释放锁。

Redis深入学习笔记-02(分布式锁)

        但是有个问题,如果逻辑执行到中间出现异常了,可能会导致del指令没有被调用,这样就会陷入死锁,锁永远得不到释放。 

        于是我们在拿到锁之后,再给锁加上一个过期时间,比如5s,这样即使中间出现异常也可以保证5s之后锁会自动释放。

Redis深入学习笔记-02(分布式锁)

        但是以上逻辑还有问题。如图下图所示,如果在setnx和 expire之间服务器程突然挂掉了,可能是因为机器掉电或者是人为造成的,就会导致expire得不到执行也会造成死锁。 

Redis深入学习笔记-02(分布式锁)

         问题的根源其实就是setnx和 expire是两条指令而不是原子指令。如果这两条指令可以一起执行就不会出现问题。

        为了解决这种问题,作者加入了set指令的扩展参数。

Redis深入学习笔记-02(分布式锁)

        这样setnx和expire组合在一起成为了原子指令。

        set name xiaotu ex 100 nx  //设置name=xiaotu ,不存在时设置,失效时常100s

超时问题:

        Redis的分布式锁不能解决超时问题,如果在加锁和释放锁之间的逻辑执行得太长,以至于超出了锁的超时限制,就会出现问题。因为这时候第一个线程持有的锁过期了,临界区的逻辑还没有执行完,而同时第二个线程就提前重新持有了这把锁,导致临界区代码不能得到严格串行执行。

可重入性

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

         后续再对分布式锁做进一步深入。

 

 

 

上一篇:redis内存回收


下一篇:AOP结合redis实现自动存取