redis 分布式锁的核心命令

redis分布式锁的实现主要是依靠setnxexpire两个命令完成。

注意:由于setnxexpire是两个命令,会存在如果 setnx 是成功的,但是 expire 设置失败,一旦出现了释放锁失败,或 者没有手工释放,那么这个锁永远被占用,其他线程永远也抢不到锁。这种情况有两种解决办法:

  1. 使用set命令时,同时设置过期时间。不再单独是使用expire命令。set key value [EX seconds] [PX milliseconds] [NX|XX]

    EX seconds:设置失效时长,单位秒 、PX milliseconds:设置失效时长,单位毫秒

    NX:key不存在时设置value,成功返回OK,失败返回(nil)

    XX:key存在时设置value,成功返回OK,失败返回(nil)

  2. 使用lua脚本,将加锁的命令放在lua脚本中原子性的执行。

redission 框架

redission 是redis的第三方库,是基于Netty实现的,是高性能的第三方库。其特点:

  • 支持 Redis 单节点(single)模式、哨兵(sentinel)模式、主从(Master/Slave)模式以及集群 (Redis Cluster)模式;
  • 提供丰富的分布式集合,如:Map,Multimap,Set,SortedSet,List,Deque,Queue 等

redission 中的分布式锁种类

总体的Redisson框架的分布式锁类型,大致如下:

  • 可重入锁
  • 公平锁
  • 联锁
  • 红锁
  • 读写锁
  • 信号量、可过期信号量
  • 闭锁(CountDownLatch)

可重入锁(Reentrant Lock)

Redisson的分布式可重入锁RLock Java对象实现了java.util.concurrent.locks.Lock接口,同时还支持自动过期解锁

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

公平锁(Fair Lock)

redission中的公平锁是可重入的,即可重入公平锁。同时有自动过期解锁的功能。

在这里插入图片描述

联锁(MultiLock)

Redisson的RedissonMultiLock对象可以将多个RLock对象关联为一个联锁,每个RLock对象实例可以 来自于不同的Redisson实例。
在这里插入图片描述

红锁(RedLock)

Redisson的RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock对 象关联为一个红锁,每个RLock对象实例可以来自于不同的Redisson实例

public void testRedLock(RedissonClient redisson1,RedissonClient redisson2,
RedissonClient redisson3){
    RLock lock1 = redisson1.getLock("lock1");
    RLock lock2 = redisson2.getLock("lock2");
    RLock lock3 = redisson3.getLock("lock3");
	RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
    
    try {
        // 方式1:同时加锁:lock1 lock2 lock3, 红锁在大部分节点上加锁成功就算成功。
        lock.lock();
        // 方式2:尝试加锁,最多等待100秒,上锁以后10秒自动解锁
        boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
    	e.printStackTrace();
    } finally {
    	lock.unlock();
    }
}

读写锁(ReadWriteLock)

Redisson的分布式可重入读写锁RReadWriteLock,Java对象实现了java.util.concurrent.locks.ReadWriteLock接口。同时还支持自动过期解锁。该对象允许同时有多个读取锁,但是最多只能有一个写入锁。
在这里插入图片描述

信号量(Semaphore)

Redisson的分布式信号量(Semaphore)Java对象RSemaphore采用了与 java.util.concurrent.Semaphore相似的接口和用法。

信号量主要用于限制对有限资源的并发访问。例如,如果有一组共享的数据库连接资源,而你希望最多只有特定数量的线程能够同时使用这些连接,就可以使用信号量来进行控制。

在这里插入图片描述

可过期信号量

Redisson的可过期性信号量(PermitExpirableSemaphore)实在RSemaphore对象的基础上,为每个 信号增加了一个过期时间。每个信号可以通过独立的ID来辨识,释放时只能通过提交这个ID才能释放。
在这里插入图片描述

闭锁/倒数闩(CountDownLatch)

Redisson的分布式闭锁(CountDownLatch)Java对象RCountDownLatch采用了与 java.util.concurrent.CountDownLatch相似的接口和用法。
在这里插入图片描述

细说 红锁

RedLock算法思想:

  1. 在分布式环境下,同时向多个redis节点申请锁;
  2. 只有当得到的锁 大于等于 n/2 + 1时(得到过半的redis节点的锁),才算是这个整体的RedLock加锁成功。
  3. 释放锁时,客户端会向所有获取锁时涉及的 Redis 节点发送释放锁的请求。但是,只要大部分节点(通常是超过半数的节点)成功释放了锁,就认为释放锁的操作是成功的。

解决的问题:红锁避免了说仅仅在一个redis实例上 加锁而带来的问题(单一实例出现故障,就会导致加锁出问题)。

红锁的缺点(被弃用):

  1. 时钟同步的问题:红锁的实现依赖于各个节点上的时钟同步。如果节点之间的时钟存在较大偏差,可能会导致锁的获取和释放出现问题。例如,如果一个节点的时钟比其他节点快,可能会导致锁提前过期,从而影响系统的正确性。

  2. 网络分区延迟问题:客户端与某一个redis节点通信出现延迟,会导致不能正确感知是否获得锁。这会影响对 “大多数节点成功获取锁” 这一条件的判断,从而导致不确定是否真正获得了锁。

  3. 节点故障处理:如果故障节点在锁的获取或释放过程中扮演了重要角色,可能会导致锁的状态不一致,从而影响系统的可靠性。(虽然红锁算法通常会尝试在节点故障时进行容错处理,但这种处理可能并不总是完美的,并且可能会引入额外的复杂性和不确定性)

  4. 官方认为它没有经过充分的检验。

红锁的替代方案

红锁被弃用,那可以用什么替代呢?

答:

  1. 用单实例的redis锁;
  2. 用普通的锁,如setNX、或者 redission。

redis 分段锁

Redis分段锁(Sharded Lock 或 Partitioned Lock) 是一种针对高并发场景下的锁机制优化方案。其核心思想是将全局锁分解为多个子锁(段锁),从而降低锁的粒度,减少锁的竞争,提升系统的并发性能。

它可以用于秒杀系统中。Redis分段锁可以有效地在这种高并发场景下提高性能,并确保数据一致性。

举例场景:短时间内,每秒商品A的订单有6000个订单。在该场景中使用redis分段锁的核心点:

  1. 分段:可以基于用户ID或者订单ID来做哈希分段,这样不同的订单就会被分配到不同的锁段中,减少锁的粒度。
    在这里插入图片描述

  2. 每个分段使用一个独立的Redis分布式锁,每个线程只对某一个分段加锁,这样多个线程可以并发操作库存。

而业务流程是:

  1. 订单进入系统:每个订单到达时,首先根据订单ID计算哈希值,确定其属于哪个分段。

  2. 获取分段锁:通过 Redis 分布式锁机制,获取订单对应分段的锁。

    扣减库存:在获取锁成功后,扣减商品库存。

    生成订单:生成订单并保存到数据库。

    释放锁:操作完成后,释放锁,允许其他请求进入该锁段。

超卖问题

分段锁机制减少了订单之间的锁竞争,但需要确保商品库存的一致性。可以将商品库存管理集中在Redis中,以确保每次扣减操作都是原子操作,避免超卖。

也就是异步库存扣减,库存的扣减先存储在redis中,等待并发量小的时候,才同步到数据库。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部