RedisTemplate和Redisson的区别

3,581 阅读25分钟

1、Redisson和RedisTemplate的什么区别

1737526156223.jpg

一、功能方面

  • Redisson

    • 提供了丰富的分布式数据结构和服务,例如分布式锁、分布式集合(包括分布式列表、集合、映射、队列、阻塞队列、双端队列、优先队列等)、分布式对象(如分布式对象、原子数、位图等)以及分布式服务(如分布式远程服务、分布式调度服务、分布式映射存储等)。它对 Redis 的操作进行了高度抽象和封装,极大地简化了分布式系统的开发。例如,使用 Redisson 的分布式锁可以这样:

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonExample {
    public static void main(String[] args) {
        // 创建 Redisson 客户端配置
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        // 创建 Redisson 客户端
        RedissonClient redisson = Redisson.create(config);
        // 获取锁对象
        RLock lock = redisson.getLock("myLock");
        try {
            // 加锁
            lock.lock();
            // 在此执行需要加锁的代码逻辑
            System.out.println("执行加锁后的操作");
        } finally {
            // 解锁
            lock.unlock();
        }
        // 关闭 Redisson 客户端
        redisson.shutdown();
    }
}

上述代码首先创建了 Redisson 的配置对象,将 Redis 服务地址添加进去,接着创建 Redisson 客户端。通过 getLock 方法获取一个锁对象,使用 lock 方法加锁,执行完操作后用 unlock 方法解锁。这样就实现了分布式锁的操作,而且 Redisson 的锁支持可重入、公平锁等高级特性,能很好地处理分布式环境下的并发问题。

  • 支持异步操作,可利用 RFuture 接口来实现异步调用,提高系统的并发性能。例如,获取锁时可以使用 lockAsync 方法,后续可对 RFuture 进行相应处理,充分利用异步操作的优势。

  • RedisTemplate

    • 主要是对 Redis 的基本数据结构(如字符串、哈希、列表、集合、有序集合)进行操作的模板类。它是 Spring Data Redis 中的一部分,更侧重于对 Redis 基本操作的封装,开发者需要根据不同的数据结构选择不同的操作接口,操作相对更加底层。例如,使用 RedisTemplate 操作哈希表:
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.HashOperations;

public class RedisTemplateExample {
    private RedisTemplate<String, Object> redisTemplate;

    public void setHashValue() {
        HashOperations<String, String, Object> hashOps = redisTemplate.opsForHash();
        hashOps.put("myHash", "key", "value");
    }
}

这里先通过 opsForHash 方法获取 HashOperations 接口,然后使用 put 方法将一个键值对放入哈希表中。其操作比较基础,对于一些复杂的分布式场景,需要开发者自己去实现相应的逻辑。

二、性能方面

  • Redisson

    • 因为提供了大量高级功能和封装,可能会带来一定的性能开销,尤其是在一些复杂的分布式操作场景下。不过,对于需要这些高级功能的分布式系统开发,它能节省大量开发时间和精力,且其性能对于大多数分布式场景是可以接受的。
    • 对于异步操作,如果使用得当,可以提高系统的并发性能,避免同步等待,减少阻塞。
  • RedisTemplate

    • 相对而言,它的性能开销可能较小,更适合一些对性能敏感且只需要使用 Redis 基本操作的简单场景。

三、使用场景方面

  • Redisson

    • 适用于开发分布式系统,如分布式锁在分布式事务、分布式任务调度、分布式缓存更新等场景中非常有用;分布式集合可用于分布式存储和处理大量数据;分布式服务可实现远程调用、分布式执行任务等。在需要大量使用 Redis 高级功能,如解决分布式环境中的并发问题、数据一致性问题、分布式服务调用等场景下表现出色。
  • RedisTemplate

    • 适合对 Redis 进行简单操作的场景,例如简单的缓存存储和读取、基本的数据结构操作等,更适合在 Spring 框架中使用,并且对于开发者想要对 Redis 操作有更多控制权,愿意自己实现一些高级功能逻辑的情况,使用 RedisTemplate 可以更好地进行自定义操作。

四、开发体验方面

  • Redisson

    • 开发体验较好,代码简洁,很多分布式功能可以通过简单的 API 调用实现,大大减少了开发分布式功能的代码量,降低了开发分布式系统的难度,提高了开发效率。
  • RedisTemplate

    • 对于熟悉 Spring 框架和 Spring Data Redis 的开发者来说,使用 RedisTemplate 比较顺手,但在处理分布式复杂功能时,需要开发者深入 Redis 命令,自己实现相关逻辑,开发难度可能会相对较高。

五、社区和维护方面

  • Redisson

    • 有自己独立的社区和维护团队,会持续更新和完善功能,对 Redis 新特性的支持也比较及时,并且在分布式领域有一定的技术积累,能为使用其的开发者提供技术支持和文档。
  • RedisTemplate

    • 依托于 Spring 社区,能随着 Spring 框架的更新而更新,但在 Redis 分布式功能的扩展上可能相对依赖 Spring 官方的支持和社区贡献,对于 Redis 自身高级功能的更新和支持可能不如 Redisson 及时。

综上所述,Redisson 更适合开发复杂的分布式系统,提供了很多高级的分布式解决方案,而 RedisTemplate 更适合简单的 Redis 操作和希望对 Redis 操作有更多自定义的开发者。

2、Redisson实现分布式锁

Redisson实现分布式锁,在redis部署使用单点/集群分片/哨兵模式下,有区别吗

Redis部署单点/集群分片/哨兵模式下,使用Redisson的方式有区别吗?
一、实现思路

  1. 对于 Redis 的不同部署模式(单点、集群分片、哨兵模式),Redisson 都提供了相应的配置方式来支持分布式锁的实现,但在细节和性能上会有一些区别。

  2. 在单点模式下,配置相对简单,直接指向单个 Redis 服务器即可。

  3. 集群分片模式下,需要配置多个 Redis 节点信息,Redisson 会自动处理数据分片和节点间的通信,确保分布式锁的可靠性和性能。

  4. 哨兵模式下,需要配置哨兵节点信息,Redisson 会根据哨兵节点来监控 Redis 主从的状态,在主节点故障时自动切换到新的主节点,保证分布式锁的可用性。

二、不同部署模式下的实现细节

  • 单点模式
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonSingleNodeLock {
    public static void main(String[] args) {
        // 创建 Redisson 配置
        Config config = new Config();
        // 设置 Redis 单点地址
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        // 创建 Redisson 客户端
        RedissonClient redisson = Redisson.create(config);
        // 获取锁对象
        RLock lock = redisson.getLock("myLock");
        try {
            // 加锁
            lock.lock();
            // 执行加锁后的业务逻辑
            System.out.println("Lock acquired, doing something...");
        } finally {
            // 解锁
            lock.unlock();
        }
        // 关闭 Redisson 客户端
        redisson.shutdown();
    }
}

上述代码中,使用 config.useSingleServer().setAddress("redis://127.0.0.1:6379") 配置了一个 Redis 单点地址,创建了 Redisson 客户端并获取锁。这种模式下,Redisson 直接与该单点 Redis 服务器通信,实现简单,但存在单点故障风险。

  • 集群分片模式
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;

public class RedissonClusterLock {
    public static void main(String[] args) {
        // 创建 Redisson 配置
        Config config = new Config();
        ClusterServersConfig clusterConfig = config.useClusterServers();
        // 添加多个 Redis 集群节点
        clusterConfig.addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001", "redis://127.0.0.1:7002");
        // 创建 Redisson 客户端
        RedissonClient redisson = Redisson.create(config);
        // 获取锁对象
        RLock lock = redisson.getLock("myLock");
        try {
            // 加锁
            lock.lock();
            // 执行加锁后的业务逻辑
            System.out.println("Lock acquired in cluster mode, doing something...");
        } finally {
            // 解锁
            lock.unlock();
        }
        // 关闭 Redisson 客户端
        redisson.shutdown();
    }
}

在集群分片模式下,通过 config.useClusterServers() 开启集群配置,使用 clusterConfig.addNodeAddress 添加多个 Redis 集群节点。Redisson 会根据 Redis 集群的特点自动处理数据的分片存储和节点间的通信,分布式锁会分布在不同的节点上,提高了系统的性能和容错性。

  • 哨兵模式
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;

public class RedissonSentinelLock {
    public static void main(String[] args) {
        // 创建 Redisson 配置
        Config config = new Config();
        SentinelServersConfig sentinelConfig = config.useSentinelServers();
        // 设置哨兵节点地址
        sentinelConfig.addSentinelAddress("redis://127.0.0.1:26379", "redis://127.0.0.1:26380");
        // 设置主节点名称
        sentinelConfig.setMasterName("mymaster");
        // 创建 Redisson 客户端
        RedissonClient redisson = Redisson.create(config);
        // 获取锁对象
        RLock lock = redisson.getLock("myLock");
        try {
            // 加锁
            lock.lock();
            // 执行加锁后的业务逻辑
            System.out.println("Lock acquired in sentinel mode, doing something...");
        } finally {
            // 解锁
            lock.unlock();
        }
        // 关闭 Redisson 客户端
        redisson.shutdown();
    }
}

在哨兵模式下,使用 config.useSentinelServers() 开启哨兵配置,通过 sentinelConfig.addSentinelAddress 添加哨兵节点地址,sentinelConfig.setMasterName 设置主节点名称。Redisson 会根据哨兵的监控信息,在主节点故障时自动切换到新的主节点,保证分布式锁在 Redis 主从切换时的有效性和可靠性。

三、区别总结

  • 单点模式

    • 配置简单,仅需指定一个 Redis 节点地址。
    • 性能和可靠性取决于单个 Redis 服务器,存在单点故障风险,一旦该节点故障,分布式锁将无法使用。
    • 适合开发测试环境或对可靠性要求不高的小型系统。
  • 集群分片模式

    • 配置多个 Redis 节点,可充分利用集群的性能和存储能力。
    • 分布式锁会根据 Redis 集群的特性在不同节点间分布,提高了系统的性能和存储容量。
    • 需要注意集群的拓扑结构和节点的状态维护,确保集群的正常运行。
    • 适合高负载、大规模数据存储和操作的系统。
  • 哨兵模式

    • 主要利用哨兵节点监控 Redis 主从状态,在主节点故障时自动切换,保证分布式锁的可用性。

    • 配置涉及哨兵节点地址和主节点名称,确保哨兵正常工作。

    • 可以提高系统的容错性,适合对系统可用性要求较高,希望在主节点故障时能自动切换到新主节点的场景。

四、使用注意事项

  1. 无论哪种模式,使用分布式锁时,都要确保加锁和解锁的操作在正确的代码块中执行,避免死锁或锁无法释放的情况。

  2. 在集群分片和哨兵模式下,要确保节点的网络和硬件配置合理,避免因网络延迟或硬件故障导致分布式锁的性能下降或失效。

  3. 对于不同的业务场景,选择合适的 Redis 部署模式和相应的 Redisson 配置,以平衡性能、可靠性和开发成本。

  4. 分布式锁的超时时间设置需要谨慎,避免因超时导致业务逻辑异常,同时要考虑不同部署模式下可能的网络延迟和 Redis 操作延迟对锁超时的影响。

总之,Redisson 在不同的 Redis 部署模式下都能实现分布式锁,但不同模式在配置、性能、可靠性等方面有各自的特点,需要根据具体的业务需求和系统架构来选择合适的模式。

3、redisson获取锁

请问 RLock lock = redisson.getLock("myLock");和 lock.lock(); 有啥区别

// 获取锁对象 
RLock lock = redisson.getLock("myLock"); 
try { 
	// 加锁 
	lock.lock(); 
	// 在此执行需要加锁的代码逻辑 
	System.out.println("执行加锁后的操作"); 
} finally { 
	// 解锁 
	lock.unlock(); 
}

一、RLock lock = redisson.getLock("myLock"); 的解释

  • 这行代码的主要目的是从 Redisson 客户端中获取一个分布式锁对象,该对象的名称是 "myLock"

  • redisson.getLock("myLock") 方法调用后,Redisson 会根据锁的名称在 Redis 中创建或查找对应的锁信息,但此时并未真正加锁。

  • 它返回的是一个 RLock 类型的对象,RLock 是 Redisson 对分布式锁的接口,实现了 java.util.concurrent.locks.Lock 接口,同时提供了一些额外的特性,如可重入性、公平锁、锁的超时等,这些特性是对 Redis 分布式锁功能的增强。

二、lock.lock(); 的解释

  • 这行代码是对之前获取到的 RLock 对象 lock 进行加锁操作。

  • 当调用 lock.lock(); 时,Redisson 会与 Redis 服务器通信,尝试获取锁。

  • 如果锁是可用的(没有其他客户端持有该锁),当前客户端将获得该锁,后续代码将可以继续执行。

  • 如果锁已被其他客户端持有,lock.lock(); 会阻塞当前线程,直到锁被释放或超时(可设置超时时间)。

  • 它会在 Redis 中设置相应的锁键值,同时使用一些机制(如基于 Redis 的数据结构和算法)来保证分布式环境下的锁的互斥性和一致性,确保同一时间只有一个客户端可以获得该锁。

三、区别总结

  • RLock lock = redisson.getLock("myLock"); 是获取锁对象的操作,是为加锁做准备,它只是一个对象的创建和获取步骤,不涉及实际的锁竞争和锁的持有。

  • lock.lock(); 是真正的加锁操作,会触发锁的竞争机制,尝试在 Redis 上获取锁,可能会阻塞线程,直到获取到锁或者超时。

四、示例代码的整体流程解释

// 获取锁对象
RLock lock = redisson.getLock("myLock");
try {
    // 加锁
    lock.lock();
    // 在此执行需要加锁的代码逻辑
    System.out.println("执行加锁后的操作");
} finally {
    // 解锁
    lock.unlock();
}

  1. 首先通过 redisson.getLock("myLock"); 获取名为 "myLock" 的分布式锁对象 lock

  2. 然后使用 lock.lock(); 对这个锁进行加锁操作,如果当前没有其他客户端持有该锁,当前客户端会成功加锁,之后执行 System.out.println("执行加锁后的操作"); 等业务逻辑。

  3. 最后在 finally 块中调用 lock.unlock(); 确保锁会被释放,无论业务逻辑是否正常执行,这是一个良好的编程习惯,防止因异常导致锁无法释放,影响其他客户端对锁的使用。

总之,getLock 是为加锁做准备,lock 是实际的加锁操作,两者结合使用可以实现 Redisson 分布式锁的获取和使用,保障分布式环境下的资源竞争和互斥访问。

4、getLock、getSpinLock、getRedLock、getReadWriteLock区别

Redisson在分布式获取锁的时候,有多个获取锁的方式: getLock、getSpinLock、getRedLock、getReadWriteLock。
在获取分布式锁业务的场景下,更适合哪种方式?顺便讲解下各自的用法

一、不同锁的特点及用法

  • getLock

    • 特点:这是最常用的获取锁的方式,提供了一个可重入锁,支持公平锁和非公平锁。可重入锁意味着一个线程可以多次获取同一个锁而不会产生死锁,只要它每次都能正确释放锁。它可以设置锁的超时时间,以防止死锁情况。

    • 用法

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonGetLockExample {
    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);
        // 获取锁对象
        RLock lock = redisson.getLock("myLock");
        try {
            // 加锁,可设置超时时间,防止死锁
            lock.lock(10, TimeUnit.SECONDS); 
            // 执行需要加锁的业务逻辑
            System.out.println("Business logic under getLock");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 解锁
            lock.unlock();
        }
        redisson.shutdown();
    }
}

上述代码通过 redisson.getLock("myLock") 获取一个普通的可重入锁,使用 lock(10, TimeUnit.SECONDS) 加锁并设置 10 秒超时时间,在 finally 块中解锁。它适用于一般的分布式资源竞争场景,如多个线程或服务需要互斥访问某个共享资源。

  • getSpinLock

    • 特点:这是一种自旋锁,当线程尝试获取锁失败时,会不断循环尝试获取锁,而不是阻塞线程。自旋锁在锁持有时间较短的情况下,可以减少线程上下文切换的开销,但如果锁持有时间较长,会消耗大量的 CPU 资源。
    • 用法

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonGetSpinLockExample {
    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);
        // 获取自旋锁对象
        RLock spinLock = redisson.getSpinLock("mySpinLock");
        try {
            // 加锁
            spinLock.lock(); 
            // 执行需要加锁的业务逻辑
            System.out.println("Business logic under getSpinLock");
        } finally {
            // 解锁
            spinLock.unlock();
        }
        redisson.shutdown();
    }
}

在这个示例中,使用 redisson.getSpinLock("mySpinLock") 获取自旋锁,调用 lock() 方法尝试加锁,线程会在获取锁失败时进行自旋等待。适用于锁竞争不激烈且锁持有时间较短的场景,例如在高并发下对一些快速操作的资源进行加锁。

  • getRedLock

    • 特点:RedLock 是一种基于多个 Redis 节点的分布式锁算法,用于解决单点故障和数据不一致的问题。它会在多个 Redis 节点上同时尝试获取锁,只有在大多数节点上成功获取锁时才认为加锁成功,提高了锁的可靠性。
    • 用法
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.api.RedissonReactiveClient;
import org.redisson.api.RedissonRxClient;
import org.redisson.config.Config;

public class RedissonGetRedLockExample {
    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redissonClient1 = Redisson.create(config);
        // 假设这里有多个 Redisson 客户端连接不同的 Redis 节点
        RedissonClient redissonClient2 = Redisson.create(config);
        RedissonClient redissonClient3 = Redisson.create(config);

        RLock lock1 = redissonClient1.getLock("myRedLock");
        RLock lock2 = redissonClient2.getLock("myRedLock");
        RLock lock3 = redissonClient3.getLock("myRedLock");

        RLock redLock = redisson.getRedLock(lock1, lock2, lock3);
        try {
            // 加锁
            redLock.lock(); 
            // 执行需要加锁的业务逻辑
            System.out.println("Business logic under getRedLock");
        } finally {
            // 解锁
            redLock.unlock();
        }
        redissonClient1.shutdown();
        redissonClient2.shutdown();
        redissonClient3.shutdown();
    }
}

在这个例子中,首先创建多个 Redisson 客户端连接不同的 Redis 节点,然后通过 redisson.getRedLock(lock1, lock2, lock3) 生成一个 RedLock,它会在多个节点上尝试加锁。适用于对锁的可靠性要求极高,不能容忍单点故障的场景,比如在分布式系统中对关键资源的保护,需要保证锁的强一致性和高可用性。

  • getReadWriteLock

    • 特点:提供了读写锁功能,读写锁允许多个读操作同时进行,但写操作是独占的,读写互斥。读锁可以被多个线程同时持有,但写锁只能被一个线程持有,且写锁的优先级高于读锁。
    • 用法
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonGetReadWriteLockExample {
    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);
        // 获取读写锁对象
        RReadWriteLock readWriteLock = redisson.getReadWriteLock("myReadWriteLock");
        // 获取写锁
        RLock writeLock = readWriteLock.writeLock(); 
        try {
            // 加写锁
            writeLock.lock(); 
            // 执行写操作的业务逻辑
            System.out.println("Write operation under getReadWriteLock");
        } finally {
            // 解锁
            writeLock.unlock();
        }
        // 获取读锁
        RLock readLock = readWriteLock.readLock(); 
        try {
            // 加读锁
            readLock.lock(); 
            // 执行读操作的业务逻辑
            System.out.println("Read operation under getReadWriteLock");
        } finally {
            // 解锁
            readLock.unlock();
        }
        redisson.shutdown();
    }
}

在上述代码中,使用 redisson.getReadWriteLock("myReadWriteLock") 获取读写锁对象,通过 writeLock() 获取写锁,通过 readLock() 获取读锁。适用于读多写少的场景,例如在分布式缓存中,多个线程可以同时读取数据,但写数据时需要独占锁,以保证数据的一致性。

二、适合的场景总结

  • getLock:适合大多数普通的分布式锁场景,对锁的可靠性和性能有一定要求,但没有特殊的分布式锁需求。

  • getSpinLock:适用于锁竞争不激烈且锁持有时间短的情况,通过自旋避免线程切换,提高性能,但长时间等待会消耗大量 CPU。

  • getRedLock:适合对锁的可靠性和容错性要求极高的场景,例如金融交易系统、分布式任务调度等,防止单点故障对锁的影响。

  • getReadWriteLock:适合读写分离的场景,如分布式数据库、缓存等系统,在读多写少的情况下,优化锁的使用,提高并发性能。

根据不同的业务需求和场景,可以选择不同的 Redisson 锁获取方式,确保分布式系统中资源的互斥访问和一致性。

5、使用Redisson,redis数据库都不设置密码吗

一、设置 Redis 密码的必要性
在生产环境中,为了保证 Redis 数据库的安全性,通常是需要设置密码的。不设置密码会导致 Redis 数据库面临安全风险,可能被未授权的用户访问和操作,进而影响数据的安全性和系统的稳定性。

二、使用 Redisson 时设置 Redis 密码的方法
以下是在 Redisson 中设置 Redis 密码的示例代码:

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonGetReadWriteLockExample {
    public static void main(String[] args) {
        Config config = new Config();
        // 设置 Redis 单点服务器信息并添加密码
        config.useSingleServer()
             .setAddress("redis://127.0.0.1:6379")
             .setPassword("yourRedisPassword"); 
        RedissonClient redisson = Redisson.create(config);
        // 获取读写锁对象
        RReadWriteLock readWriteLock = redisson.getReadWriteLock("myReadWriteLock");
        // 获取写锁
        RLock writeLock = readWriteLock.writeLock(); 
        try {
            // 加写锁
            writeLock.lock(); 
            // 执行写操作的业务逻辑
            System.out.println("Write operation under getReadWriteLock");
        } finally {
            // 解锁
            writeLock.unlock();
        }
        // 获取读锁
        RLock readLock = readWriteLock.readLock(); 
        try {
            // 加读锁
            readLock.lock(); 
            // 执行读操作的业务逻辑
            System.out.println("Read operation under getReadWriteLock");
        } finally {
            // 解锁
            readLock.unlock();
        }
        redisson.shutdown();
    }
}

上述代码在 config.useSingleServer() 之后,通过 .setPassword("yourRedisPassword") 方法设置了 Redis 的密码,你需要将 "yourRedisPassword" 替换为实际的 Redis 密码。

三、代码解释

  1. 首先,我们创建了一个 Redisson 的配置对象 Config

  2. 然后使用 config.useSingleServer() 来指定 Redis 是单点服务器模式,并使用 setAddress() 方法设置 Redis 服务器的地址。

  3. 通过 setPassword() 方法将 Redis 的密码设置进去,确保只有提供正确密码的客户端才能连接和操作 Redis。

  4. 接着使用 Redisson.create(config) 创建 Redisson 客户端。

  5. 后续的代码与之前一样,获取读写锁、加锁、执行业务逻辑、解锁操作。

四、不同部署模式下设置密码的注意事项

  • 单点模式

    • 如上述示例,直接使用 setPassword() 即可。
  • 集群模式

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;

public class RedissonClusterWithPassword {
    public static void main(String[] args) {
        Config config = new Config();
        ClusterServersConfig clusterConfig = config.useClusterServers();
        // 设置集群节点地址
        clusterConfig.addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001", "redis://127.0.0.1:7002");
        // 设置密码
        clusterConfig.setPassword("yourRedisPassword"); 
        RedissonClient redisson = Redisson.create(config);
        // 后续操作,如获取锁等,与单点模式类似
        //...
        redisson.shutdown();
    }
}

这里使用 clusterConfig.setPassword() 来设置集群模式下的 Redis 密码。

  • 哨兵模式
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;

public class RedissonSentinelWithPassword {
    public static void main(String[] args) {
        Config config = new Config();
        SentinelServersConfig sentinelConfig = config.useSentinelServers();
        // 设置哨兵节点地址
        sentinelConfig.addSentinelAddress("redis://127.0.0.1:26379", "redis://127.0.0.1:26380");
        // 设置主节点名称
        sentinelConfig.setMasterName("mymaster");
        // 设置密码
        sentinelConfig.setPassword("yourRedisPassword"); 
        RedissonClient redisson = Redisson.create(config);
        // 后续操作,如获取锁等,与单点模式类似
        //...
        redisson.shutdown();
    }
}

在哨兵模式下,使用 sentinelConfig.setPassword() 来设置密码。

五、总结
在使用 Redisson 时,无论 Redis 采用何种部署模式,都应该设置密码以保障 Redis 数据库的安全性。通过上述方法,可以方便地在不同的 Redis 部署模式下为 Redisson 配置相应的密码,从而使 Redisson 客户端在连接 Redis 时提供正确的密码信息,确保连接的安全性。

6、Redisson使用公平锁 怎样获取锁

一、使用 Redisson 获取公平锁的实现思路

  1. 首先创建 Redisson 的配置对象 Config

  2. 使用 config.useSingleServer() 或相应的集群、哨兵模式配置方法设置 Redis 服务器信息。

  3. 创建 Redisson 客户端。

  4. 通过 redisson.getFairLock("yourLockName") 获取公平锁对象。

  5. 调用 lock() 方法加锁,执行需要加锁的业务逻辑。

  6. finally 块中使用 unlock() 方法解锁,确保锁被正确释放。

二、具体代码示例

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonFairLockExample {
    public static void main(String[] args) {
        // 创建 Redisson 配置对象
        Config config = new Config();
        // 假设使用单点 Redis 服务器,设置服务器地址
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        // 创建 Redisson 客户端
        RedissonClient redisson = Redisson.create(config);
        // 获取公平锁对象,指定锁名称
        RLock fairLock = redisson.getFairLock("myFairLock");
        try {
            // 加锁
            fairLock.lock();
            // 执行需要加锁的业务逻辑
            System.out.println("Business logic under fair lock");
        } finally {
            // 解锁
            fairLock.unlock();
        }
        // 关闭 Redisson 客户端
        redisson.shutdown();
    }
}

三、代码解释

  1. Config config = new Config();:创建一个 Redisson 的配置对象,用于后续的配置。

  2. config.useSingleServer().setAddress("redis://127.0.0.1:6379");:设置 Redis 服务器为单点模式,并指定服务器的地址,你可以根据实际情况修改该地址。

  3. RedissonClient redisson = Redisson.create(config);:使用配置对象创建 Redisson 客户端。

  4. RLock fairLock = redisson.getFairLock("myFairLock");:通过 redisson.getFairLock("myFairLock") 方法获取一个名为 myFairLock 的公平锁对象。公平锁的特点是按照请求锁的顺序来分配锁,避免了某些线程长时间等待锁的情况,保证了锁分配的公平性。

  5. fairLock.lock();:调用 lock() 方法加锁,若当前锁已被其他线程持有,该线程会等待,直到获取到锁。

  6. finally 块中调用 fairLock.unlock(); 解锁,确保锁能正常释放,避免死锁和资源泄漏。

  7. redisson.shutdown();:关闭 Redisson 客户端,释放相关资源。

四、使用说明

  • 你可以根据需要修改 setAddress 中的 Redis 服务器地址,使其符合你的 Redis 部署。

  • 对于锁的名称 myFairLock,可以根据不同的业务场景进行修改,确保不同的业务使用不同的锁名称,避免锁冲突。

  • try 块中添加你需要加锁的业务逻辑,如数据库操作、资源访问等,确保这些操作在加锁期间是线程安全的。

使用公平锁可以使线程按照请求的顺序获取锁,在一些对锁获取顺序有要求的场景中非常有用,例如在任务调度、资源排队分配等场景中,能保证各个任务或资源的公平性和有序性。同时,使用 try-finally 结构确保锁的正确释放,是保证程序健壮性的重要手段。

7、Redisson在使用 公平锁与非公平锁的区别 一、公平锁与非公平锁的基本概念

  • 公平锁

    • 公平锁是指多个线程在获取锁时,会按照请求锁的顺序依次获得锁。它会维护一个等待队列,当锁被释放时,会将锁分配给等待时间最长的线程,确保了锁分配的公平性。在高并发情况下,线程等待锁的时间相对更加平均,避免了某些线程长时间无法获取锁的情况。
  • 非公平锁

    • 非公平锁不考虑线程请求锁的顺序,线程在获取锁时会直接尝试获取,获取不到才会进入等待队列。这可能导致某些线程频繁获取锁,而其他线程长时间等待,甚至出现 “饥饿” 现象,但在性能上可能会比公平锁稍好一些,因为减少了维护等待队列的开销。

二、使用 Redisson 实现公平锁和非公平锁的区别

  • 公平锁的使用示例
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonFairLockExample {
    public static void main(String[] args) {
        // 创建 Redisson 配置对象
        Config config = new Config();
        // 假设使用单点 Redis 服务器,设置服务器地址
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        // 创建 Redisson 客户端
        RedissonClient redisson = Redisson.create(config);
        // 获取公平锁对象,指定锁名称
        RLock fairLock = redisson.getFairLock("myFairLock");
        try {
            // 加公平锁
            fairLock.lock();
            // 执行需要加锁的业务逻辑
            System.out.println("Business logic under fair lock");
        } finally {
            // 解锁
            fairLock.unlock();
        }
        // 关闭 Redisson 客户端
        redisson.shutdown();
    }
}

在上述代码中,通过 redisson.getFairLock("myFairLock") 获取公平锁对象,然后使用 lock() 方法加锁,执行完业务逻辑后通过 unlock() 方法解锁。公平锁保证了线程按照请求顺序获取锁,当多个线程同时竞争时,会依次获取锁,不会出现插队现象。

  • 非公平锁的使用示例
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonNonFairLockExample {
    public static void main(String[] args) {
        // 创建 Redisson 配置对象
        Config config = new Config();
        // 假设使用单点 Redis 服务器,设置服务器地址
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        // 创建 Redisson 客户端
        RedissonClient redisson = Redisson.create(config);
        // 获取普通锁对象,默认为非公平锁,指定锁名称
        RLock nonFairLock = redisson.getLock("myNonFairLock");
        try {
            // 加非公平锁
            nonFairLock.lock();
            // 执行需要加锁的业务逻辑
            System.out.println("Business logic under non-fair lock");
        } finally {
            // 解锁
            nonFairLock.unlock();
        }
        // 关闭 Redisson 客户端
        redisson.shutdown();
    }
}

这里通过 redisson.getLock("myNonFairLock") 获取普通锁对象,它默认是非公平锁,加锁和解锁的操作与公平锁类似,但在多线程竞争锁时,可能出现线程不按顺序获取锁的情况。

三、性能和使用场景的区别

  • 性能方面

    • 公平锁:由于需要维护等待队列,在锁的分配和管理上会有额外的开销,性能可能稍逊一筹,尤其是在高并发且锁竞争激烈的情况下,可能会因为频繁的队列操作而降低性能。
    • 非公平锁:无需维护等待队列,减少了额外的操作,性能相对较好,对于性能要求较高且锁持有时间较短、锁竞争不那么激烈的场景下,可能会表现得更好。
  • 使用场景方面

    • 公平锁:适用于对锁获取顺序有要求的场景,例如在任务调度、资源分配等业务场景中,需要保证每个任务或线程按照请求的先后顺序获取锁,避免某些任务长时间等待,保证系统的公平性和有序性。

    • 非公平锁:适用于对性能要求较高,对锁获取顺序没有严格要求的场景,例如在一些短时间的操作、并发度较高但对公平性要求不高的场景下,可以减少锁获取的等待时间,提高系统的整体性能。

总之,选择公平锁还是非公平锁需要根据具体的业务需求和性能要求来决定。如果需要保证线程获取锁的公平性,避免某些线程长时间等待,可以使用公平锁;如果对性能有更高的要求,对锁获取顺序没有严格要求,可以使用非公平锁。

7、RLock.unlock使用

当获取到的锁,已经过期了,再执行releaseLock时,可以先检查下再执行releaseLock吗?防止release的时候报错

public void releaseLock(RLock lock){ 
    lock.unlock(); 
}

一、解决思路

  1. 在执行 releaseLock 方法时,需要先检查当前线程是否持有锁,以避免在锁已过期或已被其他线程持有(如因超时释放)的情况下执行解锁操作而导致错误。

  2. 可以使用 RLock 提供的 isHeldByCurrentThread 方法来检查当前线程是否持有锁。

二、修改后的代码

java

import org.redisson.api.RLock;

public class RedissonLockUtils {
    public void releaseLock(RLock lock) {
        // 检查当前线程是否持有锁
        if (lock!= null && lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

三、代码解释

  1. public void releaseLock(RLock lock):定义了一个释放锁的方法,接收一个 RLock 类型的锁对象作为参数。

  2. if (lock!= null && lock.isHeldByCurrentThread()):首先检查 lock 对象是否不为空,并且使用 isHeldByCurrentThread 方法检查当前线程是否持有该锁。

    • lock!= null:避免 null 引用导致的 NullPointerException
    • isHeldByCurrentThread():该方法会检查当前线程是否持有锁。如果当前线程持有锁,该方法返回 true,否则返回 false
  3. lock.unlock();:当满足上述条件时,执行解锁操作,确保解锁操作的安全性。

四、使用说明

  • 当调用 releaseLock 方法时,将获取到的 RLock 对象作为参数传递给该方法。例如:
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class Main {
    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);
        RLock lock = redisson.getLock("myLock");
        RedissonLockUtils lockUtils = new RedissonLockUtils();
        try {
            lock.lock();
            // 业务逻辑
        } finally {
            lockUtils.releaseLock(lock);
        }
        redisson.shutdown();
    }
}

在上述代码中,先创建了一个 RedissonLockUtils 类的实例 lockUtils,在 finally 块中调用 lockUtils.releaseLock(lock) 方法释放锁。在 releaseLock 方法中,会先检查当前线程是否持有锁,只有当当前线程持有锁时才会执行解锁操作,避免了因锁过期或被其他线程释放后再解锁而引发的异常。

通过这种方式,可以确保解锁操作的正确性和安全性,避免在锁已失效的情况下进行不必要的解锁操作,从而防止报错,提高程序的健壮性。