• 从零开发短视频电商 分布式锁-基于Redis实现



    书接上文 — > 从零开发短视频电商 分布式锁-基于数据库实现

    实现

    相关文档https://redis.io/docs/manual/patterns/distributed-locks/

    加锁

    在Redis中加锁非常简便,直接使用SET命令即可。示例及关键选项说明如下:

    SET resource_1 random_value NX EX 5
    
    • 1
    参数/选项说明
    resource_1分布式锁的key,只要这个key存在,相应的资源就处于加锁状态,无法被其它客户端访问。
    random_value一个随机字符串,不同客户端设置的值不能相同。 uuid:线程id 参考 redisson
    EX设置过期时间,单位为秒。您也可以使用PX选项设置单位为毫秒的过期时间。
    NX如果需要设置的key在Redis中已存在,则取消设置。

    示例代码为resource_1这个key设置了5秒的过期时间,如果客户端不释放这个key,5秒后key将过期,锁就会被系统回收,此时其它客户端就能够再次为资源加锁并访问资源了。

    Spring可以直接使用 redisTemplate.opsForValue().setIfAbsent(K key, V value, Duration timeout)

    解锁

    解锁一般使用DEL命令,一个客户端设置的锁,必须由自己解开。因此客户端需要先使用GET命令确认锁是不是自己设置的,然后再使用DEL解锁。在Redis中通常需要用Lua脚本来实现自锁自解:

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

    续租

    当客户端发现在锁的租期内无法完成操作时,就需要延长锁的持有时间,进行续租(renew)。同解锁一样,客户端应该只能续租自己持有的锁。在Redis中可使用如下Lua脚本来实现续租:

    续租 需要每个锁后台定时任务 时间建议是锁的1/3 去续租

    if redis.call("get",KEYS[1]) == ARGV[1] then
        return redis.call("expire",KEYS[1], ARGV[2])
    else
        return 0
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5

    集群或主从一致性问题

    Redis的主从同步(replication)是异步进行的,如果向master发送请求修改了数据后master突然出现异常,发生高可用切换,缓冲区的数据可能无法同步到新的master(原replica)上,导致数据不一致。如果丢失的数据跟分布式锁有关,则会导致锁的机制出现问题,从而引起业务异常。下文介绍两种保障一致性的方法。

    使用红锁(RedLock)

    红锁是Redis作者提出的一致性解决方案。红锁的本质是一个概率问题:如果一个主从架构的Redis在高可用切换期间丢失锁的概率是k%,那么相互独立的N个Redis同时丢失锁的概率是多少?如果用红锁来实现分布式锁,那么丢锁的概率是(k%)^N。Redis节点越多则一致性越强,鉴于Redis极高的稳定性,此时的概率已经完全能满足产品的需求。

    说明 红锁的实现并非这样严格,一般保证M(1个同时锁上即可,但通常仍旧可以满足需求。

    红锁的问题在于:

    • 加锁和解锁的延迟较大。
    • 难以在集群版或者标准版(主从架构)的Redis实例中实现。
    • 占用的资源过多,为了实现红锁,需要创建多个互不相关的云Redis实例或者自建Redis。

    使用WAIT命令

    Redis的WAIT命令会阻塞当前客户端,直到这条命令之前的所有写入命令都成功从master同步到指定数量的replica,命令中可以设置单位为毫秒的等待超时时间,实现成本低。在云Redis版中使用WAIT命令提高分布式锁一致性的示例如下:

    SET resource_1 random_value NX EX 5
    WAIT 1 5000
    
    • 1
    • 2

    使用以上代码,客户端在加锁后会等待数据成功同步到replica才继续进行其它操作,最大等待时间为5000毫秒。执行WAIT命令后如果返回结果是1则表示同步成功,无需担心数据不一致。相比红锁,这种实现方法极大地降低了成本。

    需要注意的是:

    • WAIT只会阻塞发送它的客户端,不影响其它客户端。
    • WAIT返回正确的值表示设置的锁成功同步到了replica,但如果在正常返回前发生高可用切换,数据还是可能丢失,此时WAIT只能用来提示同步可能失败,无法保证数据不丢失。您可以在WAIT返回异常值后重新加锁或者进行数据校验。
    • 解锁不一定需要使用WAIT,因为锁只要存在就能保持互斥,延迟删除不会导致逻辑问题。

    最后

    说了这么多,我觉得不要造轮子的话,可以使用redisson的分布式锁,这个开源项目内置很多分布式对象,很多开源的分布式锁都是基于他来做的。它也内置了看门狗,但是如果加锁的时候指定了过期时间,那么 Redission 不会给你开启看门狗的机制

    参考:

  • 相关阅读:
    SeaTunnel 学习笔记
    【练习题】二.栈和队列
    Glide 源码解析与原理总结——Glide.with
    Python中pip在cmd命令行下无法使用的解决方案
    美颜预览卡顿问题跟踪
    Python 基础知识:语法、数据类型和控制结构
    4 行代码写 3 个NPE异常,服了
    如何3分钟,快速开发一个新功能
    Linux 使用Nginx部署Next项目,并使用pm2进程守护
    ansible 002 连接被控端 inventory ansible.cfg ansible-adhoc ansible原理
  • 原文地址:https://blog.csdn.net/abu935009066/article/details/127762211