Redis 分布式锁

分布式锁是确保在分布式系统中同一时刻只有一个线程访问共享资源的机制。本文介绍了分布式锁的四个常见问题:死锁、锁误删、锁超时并发执行和集群容错,并探讨了解决方案。同时,讨论了分布式锁的互斥性、锁失效机制、阻塞锁、可重入性和公平性等关键特性。在实现原理部分,提到了使用Redis的加锁和解锁方法,以及RedLock算法来防止单点故障。
摘要由CSDN通过智能技术生成
  1. 分布式锁概念

    在分布式系统中,同一时间只允许一个线程/进程对共享资源进行操作。例如:秒杀、积分扣减、抢红包、定时任务执行等等。

  2. 分布式锁四种雷区
    1.  死锁:加锁成功后,不知什么原因导致服务器出现宕机,未能成功释放锁,出现死锁。(方案:设置超时时间)
    2. 锁误删:只有持有当前锁的线程,才能删除锁,即:解铃还须系铃人(方案:唯一id标识当前线程)
    3. 锁超时并发执行:加锁成功后,由于代码执行非常耗时、下游服务执行慢、调用链太长或者GC(垃圾回收)耗时等原因导致锁超时,其他线程获得锁并发执行
    4. 集群容错:成功在master解锁,未能及时同步到slave节点,此时出现脑裂存在多个master节点,其他节点也可以加锁成功
  3. 分布式锁特性
    1. 互斥性:当一个线程/进程加锁成功后,其他线程/进程无法加锁,具有排他性
    2. 锁失效机制:加锁成功后,服务器宕机导致锁未能释放,服务恢复后一直获取不到锁。应设置超时时间,防止出现类似死锁情况
    3. 阻塞锁:当前资源已被加锁,其他线程/进程来加锁是否阻塞等待,还是立即返回
    4. 可重入性:当前锁的持有者是否能再次进入
    5. 公平性:加锁顺序和请求加锁顺序是否一致,还是随机抢锁
  4. 实现原理
    1. 加锁
      1. 使用set扩展命令,key:锁标识,value:持有当前锁线程标识,PX:超时时间(毫秒)。
      2. # 加锁命令
        set key value NX PX milliseconds
    2. 解锁

      1. 只有当前锁的持有者才可以执行删除操作,通过lua脚本保证了get和del命令执行的原子性操作。

      2. // 通过lua脚本,解决了 解铃还须系铃人 的问题。
                // 使用redis+lua脚本(保证原子性,减少网络开销)
                $script = <<<LUA
                    local key=KEYS[1]
                    local value=ARGV[1]
                    if(redis.call('get', key) == value)
                    then
                        return redis.call('del', key)
                    end
        LUA;
                // 执行lua脚本
                $this->redis->eval($script, [$this->lockKey, $this->lockValue], 1);
    3. 绪期(进程占用时间太长,避免并发获得锁)
      1. $script    = <<<LUA
               if (redis.call('get', KEYS[1]) == ARGV[1])
               then 
                 return redis.call('expire', KEYS[1], ARGV[2]) 
               else 
                 return 0 
                end
        LUA;
        $this->redis->eval($script, [$this->lockKey,$this->lockValue,$this->expireTime], 1)
    4. RedLock算法
      1. 集群脑裂:集群脑裂指因为网络问题,导致 Redis master 节点跟 slave 节点和 sentinel 集群处于不同的网络分区,因为 sentinel 集群无法感知到 master 的存在,所以将 slave 节点提升为 master 节点,此时存在两个不同的 master 节点。Redis Cluster 集群部署方式同理。

      2. 多节点redis实现的分布式锁算法(RedLock):有效防止单点故障

        假设有5个完全独立的redis主服务器

        1.获取当前时间戳

        2.client尝试按照顺序使用相同的key,value获取所有redis服务的锁,在获取锁的过程中的获取时间比锁过期时间短很多,这是为了不要过长时间等待已经关闭的redis服务。并且试着获取下一个redis实例。

           比如:TTL为5s,设置获取锁最多用1s,所以如果一秒内无法获取锁,就放弃获取这个锁,从而尝试获取下个锁

        3.client通过获取所有能获取的锁后的时间减去第一步的时间,这个时间差要小于TTL时间并且至少有3个redis实例成功获取锁,才算真正的获取锁成功

        4.如果成功获取锁,则锁的真正有效时间是 TTL减去第三步的时间差 的时间;比如:TTL 是5s,获取所有锁用了2s,则真正锁有效时间为3s(其实应该再减去时钟漂移);

        5.如果客户端由于某些原因获取锁失败,便会开始解锁所有redis实例;因为可能已经获取了小于3个锁,必须释放,否则影响其他client获取锁

        算法示意图如下:

         

 

转载于:Redlock(redis分布式锁)原理分析_狂奔的蜗牛Evan的博客-CSDN博客_redlock百度安全验证https://baijiahao.baidu.com/s?id=1706527669583273516&wfr=spider&for=pc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值