• 基于Redis的简单分布式锁的原理



    参考资料:https://redis.io/commands/setnx

    加锁是为了解决多线程的资源共享问题。Java中,单机环境的锁可以用synchronized和Lock,其他语言也都应该有自己的加锁机制。但是到了分布式环境,单机环境中的锁就没什么作用了,因为每个节点只能获取到自己机器内存中的锁,而无法获取到其他节点的锁状态。

    分布式环境中,应该用专门的分布式锁来解决需要加锁的问题。分布式锁有很多实现,Redis,zookeeper都可以。这里以Redis为例,讲述一下基于Redis的分布式锁的基本原理。

    用Redis来实现分布式锁的原因

    不同的节点无法获取到其他节点内存中的锁,但是大家都可以获取到Redis中的资源,所以这是实现分布式锁的基础-所有节点都可以同时获取到redis的状态。
    而具体的实现,则是基于两个redis的命令-SETNX和GETSET。
    SETNX:SET if Not eXists,格式为SETNX key value,仅当key不存在时才会设置成功,返回1,否则返回0。这是加锁的基础,假设key名为lock.foo,只要有一个线程设置成功,那其他线程都无法再设置。
    GETSET:GETSET key value,返回旧值,并将新的值设置进去。这个的作用后面会讲到。


    锁实现以及超时设计

    加锁方式很简单,在线程中对redis发送一个命令:

    SETNX lock.foo <current Unix time + lock timeout + 1>
    

     

    线程A调用setnx命令,设置key为lock.foo(所有线程要用同样的key,否则就不是一个锁了),值为current Unix time + lock timeout + 1,即当前时间加上加锁时长,最终的值也就是过期时间。如果A对锁的持有结束,则可自行调用del lock.foo来释放锁。

    A持有锁的过程中线程B在调用命令SETNX lock.foo,会得到返回值0,这说明这个锁已经被其他线程获取,这时B应该去获取lock.foo的值,看看是否小于当前时间,如果大于则锁未过期,B需要继续循环等待检查或者做其他操作;如果小于则锁已过期,B可以用del lock.foo方法去删除锁,然后在SETNX lock.foo 来获取锁。

    这样就完成了分布式锁的最基本的模型,并且避免了因A线程挂掉无法释放锁而导致的死锁问题。


    存在的问题

    上一节的实现看上去大致还是那么回事,成功的加上锁了,还引入了超时机制。不过,GETSET还没用呢,这肯定还没完呢。请看以下场景:

    A获取到了锁,但是挂掉了;

    B和C都检测到A的锁超时;

    B发出del lock.foo指令,删除A的锁,再setnx,获取到了锁;

    C也发出del lock.foo指令,此时删除的是B的锁,然后再setnx,获取到了锁。


    这个时候你会发现,B和C同时获取到了锁。这问题就大了去了。

    为了解决这个问题,GETSET就起到他自己的作用了。下面用修正后的方法来重新描述一下上面的场景:

    A获取到了锁,但是挂掉了;

    B和C都检测到A的锁超时;


    此时B不会执行del操作,而是执行:

    GETSET lock.foo <current Unix timestamp + lock timeout + 1>


    这个命令会给lock.foo设置新值,然后获取到老的value。这个时候B会对老的value进行检测,如果value大于当前时间,则说明这个锁已经被其他线程再次获取了,那B就会继续
    等待,而不是获取锁。如果value小于当前时间,那B就可以获取到锁。

    假设C获取到锁,然后B又再次调用GETSET方法,那也不会对C持有锁造成影响,不过确实会将超时时间延长一些。但是出现这个情况肯定是B和C都在之前检测到了锁超时,说明这两个线程对锁的访问肯定较为接近,所以这里如果要求不是太严格也可以忽略。

    基本原理就这些,代码稍后奉上。

  • 相关阅读:
    linux 批量替换内容
    在Linux下如何查CC攻击?
    mysql init_connect
    利用javascript对字符串加密
    js学习笔记10----字符串的基本操作
    js学习笔记9----时间操作
    3种方法实现列表自动滚动
    如何解决wow.js与fullpage的兼容性
    js兼容获取元素的样式
    用php去除bom头
  • 原文地址:https://www.cnblogs.com/csonezp/p/7494833.html
Copyright © 2020-2023  润新知