• Redis分布式锁的使用
  • 发布于 2个月前
  • 191 热度
    0 评论
引言
在分布式系统中,由于多节点并发执行,为确保数据的一致性和完整性,仅通过本地锁显然无法满足实际需求,这时分布式锁就是至关重要的组件,常见的分布锁Redis。

锁的条件
.多任务环境下。(进程,线程)
.任务都对同一共享资源进行写操作。

.对资源的访问是互斥的。


原理
1.A 尝试建立资源
2.在A之前没有这个资源
3.A建立资源成功,A获得了锁

4.B尝试建立资源
5.在B之前A已经建立了资源
6.B建立资源失败,B未获得锁

7.A过了一段时间删除了该资源,释放了锁

8.B尝试建立资源
9.在B之前没有这个资源

10.B建立资源成功,B获得了锁


如果Redis故障了,所有客户端无法获取锁,服务变得不可用,所以为了提高可用性,通常给Redis配置主从。

但也存在特殊场景:当master不可用时,系统切换到slave,由于Redis的主从复制(replication)是异步的,slave并未及时同步,这可能导致丧失锁的安全性。

这时Redis的作者Salvatore Sanfilippo提出RedLock算法,一种增强的分布式锁实现,主要用于解决在分布式系统中实现可靠锁的问题,它利用了多个独立 Redis 实例的时间不一致性以及多数派选举策略,旨在提供更高的安全性和容错性。

正文
算法设计的核心特性:
互斥性:在任何给定时刻,仅有一个客户端能够成功获取分布式锁,确保了对共享资源的排他性和唯一访问权,防止并发冲突。
自动避免死锁:通过为每个锁实例设置一个较短且合理的过期时间,即使在极端情况下客户端在获得锁之后由于网络中断、进程崩溃等原因未能正常释放锁,锁也会在其预设的生存期限过后自动失效。这一机制有效消除了死锁的可能性。
容错性:系统构建在多个独立的 Redis 实例之上,即便部分 Redis 节点出现故障或宕机,只要集群中的大多数节点(即超过半数以上的节点)仍然可用并能保持通信,该算法就能够继续提供有效的分布式锁服务,并保证锁操作的正确性和一致性。这使得系统在面对部分组件不可用时仍具有高可用性和可靠性。


算法设计的实现机制:

采用了对 Redis 集群中每个独立节点尝试获取锁的方法,客户端向集群中的每一个 Redis 节点请求加锁,并为锁设置一个较短的过期时间。如果客户端能在集群中超过半数以上的节点(即 N/2+1 个节点)上锁成功,那么系统就认为分布式锁获取成功。例如,在一个包含5个节点的 Redis 集群中,客户端需要在至少3个节点上成功加锁才能宣布获取分布式锁成功。

这种策略确保了即使集群中有个别节点发生故障或宕机,只要剩余的大多数节点仍持有锁的状态,分布式锁服务就能继续有效运行,保证了系统的高可用性和资源访问的互斥性。由于各个节点上的锁都有设定的过期时间,因此也能有效地防止死锁问题的发生。

基本使用
public class RedLockDemo {

    public static void main(String[] args) {
        // 堆代码 duidaima.com
        // 创建 Redisson 客户端配置
        Config config = new Config();
        config.useClusterServers()
        .addNodeAddress("redis://192.168.207.128:6379",
                        "redis://192.168.207.129:6379",
                        "redis://192.168.207.130:6379");
        // 创建 Redisson 客户端实例
        RedissonClient redissonClient = Redisson.create(config);
        // 创建 RedLock 对象
        RedissonRedLock redLock = redissonClient.getRedLock("yian");
        try {
            // 尝试获取分布式锁,最多尝试 5 秒获取锁,并且锁的有效期为 5000 毫秒
            boolean lockAcquired = redLock.tryLock(5, 5000, TimeUnit.MILLISECONDS); 
            if (lockAcquired) {
                // 加锁成功,执行业务代码...
            } else {
                System.out.println("Failed to acquire the lock!");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.err.println("Interrupted while acquiring the lock");
        } finally {
            // 无论是否成功获取到锁,在业务逻辑结束后都要释放锁
            if (redLock.isLocked()) {
                redLock.unlock();
            }
            // 关闭 Redisson 客户端连接
            redissonClient.shutdown();
        }
    }
}

用户评论