美文网首页
redis学习笔记(九) 分布式锁

redis学习笔记(九) 分布式锁

作者: 云师兄 | 来源:发表于2019-09-25 14:56 被阅读0次

    1. 引子

    在多线程环境下,如果想对共享内存中的数据进行互斥访问且想保证线程安全,Java可以使用很多种锁来实现;但是在分布式环境下,由于线程不在一个进程中,使用Java中提供的锁就没有用了,一般会使用redis的setnx来实现分布式锁。

    2. 加锁的正确姿势

    使用setnx加锁需要满足下面三个正确姿势:

    • 必须给锁设置一个失效时间:防止特殊情况下锁等不到释放从而使得其他线程死锁。
    • 其中加锁时,保存的value是一个随机字符串:这是因为可能存在这种情况:线程A先执行setnx加锁,并给key设置的超时时间为2秒,然后由于线程繁忙,3秒后才去解锁,而这个key在2秒的时候超时删除了,线程B这个时候就获取到了锁。这就可能导致3秒的时候,线程A将线程B上的锁给解了。为避免这种情况,应该在解锁的时候通过value来判断是不是自己上的锁,并要求只有自己上的锁才能解锁,不能解锁别人上的锁。
    • 写入随机值和设置超时时间必须为原子性操作:不能先设置了key value,后设置超时时间,应该一步就将这两个操作同时做了。

    3. 解锁的正确姿势

    同样的,解锁分为三步:

    1. 从redis获取数据。
    2. 判断value是否和本地保存的value一致(是不是自己上的锁)
    3. 删除该键值对

    这三步也要求保证原子性,不然在高并发环境下会有问题。一般要保证原子性,可以在redis使用lua脚步来实现:

    if redis.call("get",KEYS[1]) == ARGV[1] then
        return redis.call("del",KEYS[1])
    else
        return 0
    end
    

    4. 注意

    Redis的setnx命令本身不带expire参数以设置过期时间,因此只能先setnx获取锁后,再给这个key设置过期时间。但是由于这是两步操作,可能会导致刚执行完setnx后,redis就挂了,这种请求就会导致这个锁永远不能释放。为解决该问题,redis 2.6的版本后,支持set命令可以同时支持nx和过期时间两个操作:

    屏幕快照 2019-06-23 下午7.19.36.png

    所以使用上述set命令才能万无一失的实现分布式锁。

    相关文章

      网友评论

          本文标题:redis学习笔记(九) 分布式锁

          本文链接:https://www.haomeiwen.com/subject/qqtwuctx.html