Redis锁的介绍「建议收藏」

Redis锁的介绍「建议收藏」Redis锁的实现:由于Redis是单进程的,可以简单用setnx这个命令进行加锁操作,谁能操作成功,谁就可以获得锁。简单的代码如下:defacquire_lock():   #identifier:唯一标识客户端   #lockname锁名字   #redis客户端连接   ifredis.setnx(lockname,identifier):     …

大家好,又见面了,我是你们的朋友全栈君。

Redis锁的实现:
由于Redis是单进程的,可以简单用setnx这个命令进行加锁操作,谁能操作成功,谁就可以获得锁。简单的代码如下:
def acquire_lock():
    # identifier: 唯一标识客户端
    # lockname 锁名字
    # redis 客户端连接
    if redis.setnx(lockname, identifier):
        return True
    return False
这里有一个问题,就是如果客户端在获得锁的时候崩溃了,服务器就无法再把锁分配给其他客户端使用了,为了解决这个问题,我们可以利用redis的超时特性,给锁加上超时时间
def acquire_lock():
    # identifier: 唯一标识客户端
    # lockname 锁名字
    # redis 客户端连接
    # timeout 超时时间
    if redis.setnx(lockname, identifier):
        redis.expire(lockname, timeout)
        return True
    elif not redis.ttl(lockname):
        redis.expire(lockname, timeout)
        return False
    return False
可以这样认为,多个客户端同时设置过期时间也是差别不大的,我们在发现锁已经存在并且没有超时限制时,给锁加上超时限制,这样可以在其他客户端获得锁并未设置超时时间崩溃了,也能在过期时间到了让其他客户端获取到锁。最新官方文档支持用set命令指定超时和nx特性,
def acquire_lock():
    # identifier: 唯一标识客户端
    # lockname 锁名字
    # redis 客户端连接
    # timeout 超时时间
    if redis.set(lockname, identifier, nx=True, ex=timeout):
        return True
    return False
解锁操作,直接执行一段lua脚本
def release_lock():
    # identifier: 唯一标识客户端
    # lockname 锁名字
    # redis 客户端连接
    script = “””
        if redis.call(‘GET’, KEYS[1]) == ARGV[1] then
            return redis.call(‘DEL’, KEYS[1])
        else
            return 0
    “””
    return redis.eval(script, lockname, identifier)
使用lua脚本可以原子的操作解锁过程,这里需要注意点,eval的key是要传的,这样代码也可以在redis集群中使用,否则redis不知道lua脚本应该在哪一个槽进行执行,具体可以看官方的文档

另外一种经常使用的计数信号量的使用,最简单的方式是用一个zset,通过客户端设置过期时间
def acquire_semaphore():
    # identifier: 唯一标识客户端
    # semname 信号量集合名
    # redis 客户端连接
    # timeout 过期时间
    # limit 信号量的计数
    now = time.time()
    pipe = redis.pipeline(True)
    pipe.zremrangebyscore(semname, ‘-inf’, now-timeout)
    pipe.zadd(semname, identifier, now)
    pipe.zrank(semname, identifier)
    if pipe.execute()[-1] < limit:
        return True
    redis.zrem(semname, identifier)
    return False
这里有一个问题,就是如果某一个客户端的时间比较快,那么这个较快的客户端是有可能在信号量已经达到限制的时候获得新号量的。
比如A客户端比其他客户端快50ms,那么当最后一个信号量被取走的同时,如果这时候A请求信号量,根据代码是有可以获得信号量的,不是公平的
为了解决公平的问题,可以引入多一个有序集合,记录获取信号量的计数

def acquire_fair_semaphore():
    # identifier: 唯一标识客户端
    # semname 信号量集合名
    # redis 客户端连接
    # timeout 过期时间
    # limit 信号量的计数
    czset = semname + “:owner”
    ctr = semname + “:counter”
    now = time.time()
    pipe = redis.pipeline(True)
    pipe.zramrangebyscore(semname, ‘-inf’, now-timeout)
    pipe.zinterstore(czset, {czset:1, semname: 0})
    pipe.incr(ctr)
    counter = pipe.execute()[-1]
    pipe.zadd(semname, identifier, now)
    pipe.zadd(czset, identifier, counter)
    pipe.zrank(czset, identifier)
    if pipe.execute()[-1] < limit:
        return True
    pipe.zrem(semname, identifier)
    pipe.zrem(czset, identifier)
    pipe.execute()
    return False    
这个看似没啥问题的代码还是有一点问题,假设剩下最后一个信号量,AB两个客户端依次执行incr获得counter = 10,11
这个时候如果B先执行了zrank,那么会导致A和B都同时获得了最后一个信号量,如果场景不在乎这点小问题也没问题,不然可以在这段操作前,先获取前面的锁,再进行操作。

上面介绍的锁其实有一个假设是Redis服务器没有挂,如果Redis服务器挂了,有主从切换的话会切换到从服务器,但是从服务器并不一定与主服务器数据完全一致,取决于同步的方式,假设A获得锁之后服务器挂了,这个信息没有记录到从服务器中,从服务器起来的时候是无锁状态的,有可能会造成两个客户端同时获取了锁,一种解决方法是如果发生主从切换的话,暂停新启动的服务器使用锁的时间,超过过期时间即可。

基于Redis实现的一种分布式锁,RedLock算法
客户端以当前时间毫秒级向多个独立的Redis实例请求锁,超过实例总数(N) N/2+1的客户端获取到这把锁,而释放锁的操作只需要在所有的实例执行释放锁操作。
具体可以参考官方文档:https://redis.io/topics/distlock

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/149192.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 微信火柴人html5小游戏,20个好玩的微信小游戏推荐!你玩过几个?「建议收藏」

    50000+游戏爱好者已加入我们!每天推荐好玩游戏!加入我们,沐沐带你发现好游戏!只有你想不到,没有我找不到的好游戏!「良心好游戏推荐」搜罗了好玩的微信小游戏大全,模拟经营游戏、恐怖游戏、消除游戏、休闲游戏、益智游戏、吃鸡游戏、烧脑游戏、解谜游戏大全、换装游戏、射击游戏、吃鸡小游戏、像素游戏一个都不少!还有游戏攻略哦!每天都会推荐好玩的小游戏。————————————————————————不知不…

    2022年4月11日
    94
  • NDP和LLDP协议

    NDP和LLDP协议NDP和LLDP作用是一样,NDP:用来发现直接相连的邻居信息,包括邻接设备的设备名称、软/硬件版本、连接端口等,另外还可提供设备的id、端口地址、硬件平台等信息LLDP:它提供了一种标准的链路层发现方式,可以将本端设备的的主要能力、管理地址、设备标识、接口标识等信息组织成不同的TLV(Type/Length/Value,类型/长度/值),并封装在LLDPDU(LinkLaye…

    2022年6月2日
    59
  • 排序算法之从冒泡排序所想到的

    排序算法之从冒泡排序所想到的

    2021年12月10日
    45
  • 【论文笔记】Unmasking DeepFakes with simple Features「建议收藏」

    【论文笔记】Unmasking DeepFakes with simple Features「建议收藏」@MENGM0本文使用图像的功率谱特征实现了对高分率伪造人脸图片的检测。Introduction文章2019年11月发布在arxiv上,提出了使用图像的功率谱(PowerSpectrum)作为特征,使用简单的机器学习方法(SVM)进行伪造图像的取证。作者的Github。数据集作者制作了一个名为Faces-HQ的数据集,19GB,真实图片来自CelebA-HQ和Flickr-Faces-…

    2022年5月22日
    36
  • idea配置tomcat以及环境变量「建议收藏」

    idea配置tomcat以及环境变量「建议收藏」这里写目录标题tomcat环境变量idea配置tomcattomcat环境变量第一步下载tomcat网址:https://tomcat.apache.org/download-90.cgi下载完成后解压进入解压后的文件夹复制地址第二步下载tomcat环境变量配置右击此电脑->属性->高级系统设置->环境变量1.CATALINA_HOME2.编辑Path,点击“新建”,添加变量值:%CATALINA_HOME%\lib%CATALINA_HOME%\b

    2022年5月13日
    263
  • 阿姨想让13岁的儿子学Java编程,我拒绝了

    阿姨想让13岁的儿子学Java编程,我拒绝了大家好,我是锋哥;先爆丑照,为了你们有勇气继续阅读下去;前一段时间,有个阿姨找我学java,我懵逼了。我以为阿姨学着玩玩的,我就同意了。她说过段时间找我报名;后来,阿姨又找我阿姨想让她13岁的儿子跟我学编程,我想了下13岁应该六年级或者初一,不正是学习科学文化基础知识的时候吗。我这人有原则,有底线,也不想坑人,孩子太小,应用编程真不适合,也会耗费大量的时间和精力,耽误学业。孩子学应用编程前,应该至少有初中毕业文化,计算机简单基础,英语基础,应用软件基础,如果可以,还需要了解计算机组成原

    2022年4月30日
    57

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号