Redisson

月伴飞鱼 2024-12-16 18:34:21
框架相关
支付宝打赏 微信打赏

如果文章对你有帮助,欢迎点击上方按钮打赏作者!

Redisson是一个在Redis的基础上实现的驻内存数据网格。

它不仅提供了一系列的分布式的常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现。

官网地址: https://redisson.org

GitHub地址: https://github.com/redisson/redisson

分布式锁

分布式锁应用场景:

提升处理效率

  • 避免重复任务的执行,减少系统资源的浪费(例如幂等场景)。

保障数据一致性

  • 在多个微服务并发访问时,避免出现访问数据不一致的情况,造成数据丢失更新等情况。

以下是不同客户端并发访问时的场景:

1

分布式锁原理:

67949d5ca4eca40c0790f5f2cdd61e34

可重入实现:

Redisson 实现可重入采用 Hash 的结构,在 Key 的位置记录锁的名称。

  • Field 的位置记录线程标识, Value 的位置则记录锁的重入次数。

加锁时,如果线程标识是自己,则锁的重入次数加 1,并重置锁的有效期。

释放锁时,重入次数减 1,并判断是否为 0,如果为 0 直接删除,否则重置锁的有效期。

2

重试与WatchDog流程:

3

可重入锁(Reentrant Lock)

可重入就是说同一个线程可以多次拿到同一把锁,不会把自己锁死。

这在方法嵌套调用的时候特别有用,比如方法A调用方法B,两个方法都要加锁,用可重入锁就不会出问题。

这种锁适合什么场景呢?

  • 就是普通的分布式锁需求,保证同一时间只有一个节点在执行某段代码。

比如防止重复下单、避免重复处理同一个任务之类的。

RLock lock = redissonClient.getLock("myLock");
lock.lock();
try {
    // 处理业务
    // 如果这里又调用了需要加锁的方法,不会死锁
    lock.lock();
    try {
        // 嵌套的业务处理
    } finally {
        lock.unlock();
    }
} finally {
    lock.unlock();
}

公平锁(Fair Lock)

普通锁谁抢到算谁的,可能会出现有些请求一直抢不到锁的情况。

公平锁就不一样了,严格按照申请的顺序来分配锁,不会有人饿死。

什么时候用?

  • 主要是对顺序有要求的场景。
  • 比如订单处理,你总不能让后提交的订单先处理吧,或者一些排队系统,得保证公平性。

不过公平锁的性能会差一些,毕竟要维护一个队列。

  • 所以如果对顺序没那么严格的要求,还是用普通锁比较好。
RLock fairLock = redissonClient.getFairLock("fairLock");
fairLock.lock();
try {
    // 业务处理
} finally {
    fairLock.unlock();
}

联锁(MultiLock)

一次锁多个资源。

模拟个转账的场景:

  • 从账户A转钱到账户B,你得同时锁住这两个账户吧?

  • 如果分开锁,可能会出现死锁,线程1锁了A等B,线程2锁了B等A,谁都动不了。

联锁的策略是要么全部锁成功,要么全部失败,不会出现锁了一半的情况。

  • 这样就避免了死锁问题。

除了转账,还有什么场景会用到?

  • 比如库存扣减时需要同时锁定多个商品、或者需要同时更新多个相关的数据表等等。
RLock lock1 = redissonClient.getLock("lock1");
RLock lock2 = redissonClient.getLock("lock2");
RLock lock3 = redissonClient.getLock("lock3");

RLock multiLock = redissonClient.getMultiLock(lock1, lock2, lock3);
multiLock.lock();
try {
    // 这里可以安全地操作多个资源
} finally {
    multiLock.unlock();
}

红锁(RedLock)

在多个独立的Redis实例上同时加锁,只有大多数实例都加锁成功了,才算获取锁成功。

  • 这样即使某个Redis实例挂了,锁依然有效。

什么时候用红锁?

  • 主要是对可靠性要求特别高的场景。
  • 比如金融系统的关键操作,不能因为Redis挂了就出问题。

不过代价也很明显,你得准备多个Redis实例,成本和复杂度都上去了。

所以大部分情况下,普通的分布式锁就够用了,除非你的系统对可靠性要求极高。

RLock lock1 = redissonClient.getLock("lock");
RLock lock2 = redissonClient2.getLock("lock");
RLock lock3 = redissonClient3.getLock("lock");

RLock redLock = redissonClient.getRedLock(lock1, lock2, lock3);
redLock.lock();
try {
    // 业务处理
} finally {
    redLock.unlock();
}

读写锁(ReadWrite Lock)

多个线程可以同时拿读锁,大家一起读没问题。

但是写锁是排他的,写的时候别人不能读,读的时候也不能写。

  • 这在读多写少的场景下能大大提升性能。

典型场景就是缓存更新。

  • 大部分时候都是在读缓存,偶尔需要更新一下。

  • 用读写锁的话,读操作可以并发进行,只有更新的时候才需要独占。

还有配置文件读取、商品信息查询这些场景,都很适合用读写锁。

RReadWriteLock rwLock = redissonClient.getReadWriteLock("rwLock");

// 读操作
RLock readLock = rwLock.readLock();
readLock.lock();
try {
    // 读取数据
} finally {
    readLock.unlock();
}

// 写操作
RLock writeLock = rwLock.writeLock();
writeLock.lock();
try {
    // 写入数据
} finally {
    writeLock.unlock();
}

信号量(Semaphore)

信号量其实并不是严格意义上的锁,但经常和锁一起用,主要用来限流。

RSemaphore semaphore = redissonClient.getSemaphore("semaphore");
semaphore.trySetPermits(3); // 设置3个许可证

semaphore.acquire(); // 拿一个许可证
try {
    // 处理业务
} finally {
    semaphore.release(); // 还回去
}
支付宝打赏 微信打赏

如果文章对你有帮助,欢迎点击上方按钮打赏作者!