redisson3.15.0的引入原因及使用

本文探讨了在更新MySQL和Redis数据时可能出现的数据不一致情况,提出使用Redisson作为分布式锁来避免并发导致的问题。通过lua脚本实现的Redisson锁确保了在多线程环境下操作的串行化,从而保证数据的一致性。同时,Redisson还能作为读写锁提升性能。在项目中引入Redisson,通过添加依赖并编写测试代码展示了其实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引入redisson的背景

  • 由于redis与mysql会出现数据不一致的情况,在更新mysql数据与更新redis数据存在时间差,不同线程的时间差可能不同,例子:A线程更新mysql数据为2,此时A线程卡顿,然后B线程更新mysql数据为3,B线程更新redis数据为3,之后A线程恢复执行,A线程更新redis数据为2;此时出现数据不一致问题,即使:mysql数据为3,redis数据为2
  • 针对redis与mysql数据不一致问题;根据需求及并发量,有不同的解决方案。比如:设置数据的过期时间,延迟双删,写多读多直接不使用redis
  • 延迟双删,对于需要延迟的时间,需要设置的比业务代码执行的时间长,但是业务代码执行的时间是不定的,延迟时间设置的过长过短均会出现问题。因此可以采用分布式锁redisson
  • 在不考虑主从高可用的情况下,可用采用redisson分布式锁,将并行的代码,变成串行,即可解决数据不一致问题。例子:A线程更新mysql数据,更新redis数据执行完毕后,B线程再执行;此时不会出现数据不一致问题
  • redisson除解决数据不一致问题,还可以做为分布式锁;如果读多写少,可以使用redisson的读写锁优化性能

项目中引入redisson

  • 项目已经引入redis
  • pom中引入redisson3.15.0依赖
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.15.0</version>
</dependency>
  • 测试使用代码
        // 开始加锁,锁名称不与他人重复
        Lock lock = redissonClient.getLock("lock");
        lock.lock();
        try {
            // 执行业务代码
            Thread.sleep(1000);
        } catch (Exception e) {
            log.info("异常, {}", e.getMessage());
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }

redisson相关问题

  • 底层通过lua脚本实现
 -- 若锁不存在:则新增锁,并设置锁重入计数为1、设置锁过期时间
if (redis.call('exists', KEYS[1]) == 0) then
    redis.call('hset', KEYS[1], ARGV[2], 1);
    redis.call('pexpire', KEYS[1], ARGV[1]);
    return nil;
end;
 
-- 若锁存在,且唯一标识也匹配:则表明当前加锁请求为锁重入请求,故锁重入计数+1,并再次设置锁过期时间
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
    redis.call('hincrby', KEYS[1], ARGV[2], 1);
    redis.call('pexpire', KEYS[1], ARGV[1]);
    return nil;
end;
<think>我们计划在Spring Boot 2.4.5项目中同时使用Spring Data Redis(通过spring-boot-starter-data-redis)和Redisson。为了避免冲突,我们需要做以下步骤: 1. 添加依赖并排除冲突。 2. 分别配置Spring Data Redis和Redisson3. 在代码中分别使用两个客户端。 根据Spring Boot 2.4.5,对应的Spring Data版本是2020.0.x,因此我们需要选择适配Spring Data Redis 2.4的Redisson Spring Data模块(即redisson-spring-data-24)。 但是,Redisson官方提供的Spring Data模块适配器通常按照Spring Boot的主版本(2.1, 2.2, 2.3等)来命名。对于Spring Boot 2.4.x,我们可以尝试使用`redisson-spring-data-24`(如果存在)或者使用最接近的版本(如redisson-spring-data-23)。然而,在Redisson的GitHub仓库和文档中,我们通常看到的是按照Spring Boot的次版本(2.1, 2.2, 2.3)来命名的,并没有明确提供2.4的适配器。实际上,Redisson的Spring Data模块通常向下兼容,因此我们可以选择适配Spring Boot 2.3的模块(redisson-spring-data-23)来使用。 但是,根据Redisson的官方文档(https://github.com/redisson/redisson/tree/master/redisson-spring-data#spring-data-redis-integration),它提供了以下适配器: - redisson-spring-data-16 (for Spring Data Redis 1.6.x) - redisson-spring-data-17 (for Spring Data Redis 1.7.x) - redisson-spring-data-18 (for Spring Data Redis 1.8.x) - redisson-spring-data-20 (for Spring Data Redis 2.0.x) 对应Spring Boot 2.0.x - redisson-spring-data-21 (for Spring Data Redis 2.1.x) 对应Spring Boot 2.1.x - redisson-spring-data-22 (for Spring Data Redis 2.2.x) 对应Spring Boot 2.2.x - redisson-spring-data-23 (for Spring Data Redis 2.3.x) 对应Spring Boot 2.3.x - redisson-spring-data-24 (for Spring Data Redis 2.4.x) 对应Spring Boot 2.4.x(Redisson3.15.0版本开始提供) 因此,我们需要使用`redisson-spring-data-24`。但是请注意,Redisson版本需要>=3.15.0。 步骤: 1. 在pom.xml中添加依赖,并排除redisson-spring-data-23(因为默认可能包含的是23,我们需要替换为24)或者直接引入24。 2. 配置两个连接工厂,或者让Redisson使用自己的配置,而Spring Data Redis使用自己的配置(推荐使用两个独立的配置,避免冲突)。 具体操作: 第一步:添加依赖 在pom.xml中添加以下依赖: ```xml <!-- Spring Data Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.4.5</version> </dependency> <!-- Redisson Starter(这里使用redisson-spring-boot-starter,注意版本) --> <dependency> <groupId>org.redisson</groupId> <!-- 使用redisson-spring-boot-starter可以简化配置,但需要排除默认的Spring Data适配器 --> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.17.7</version> <exclusions> <!-- 排除可能自带的旧版本Spring Data适配器 --> <exclusion> <groupId>org.redisson</groupId> <artifactId>redisson-spring-data-*</artifactId> </exclusion> </exclusions> </dependency> <!-- 添加适配Spring Boot 2.4的Redisson Spring Data模块 --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-data-24</artifactId> <version>3.17.7</version> </dependency> ``` 注意:Redisson版本3.17.7支持Spring Boot 2.4.x。 第二步:配置 为了避免自动配置冲突,我们需要为Spring Data Redis和Redisson分别配置连接信息。 在application.properties中配置Spring Data Redis: ```properties # Spring Data Redis配置 spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password= spring.redis.database=0 ``` 然后,为Redisson单独配置。我们可以创建一个redisson.yml文件(放在resources目录下),内容如下: ```yaml singleServerConfig: address: "redis://127.0.0.1:6379" password: null database: 0 connectionMinimumIdleSize: 5 connectionPoolSize: 10 ``` 并在application.properties中指定Redisson的配置文件位置: ```properties # Redisson配置 redisson.config=classpath:/redisson.yml ``` 第三步:代码中使用 我们可以同时注入`RedisTemplate`(或`StringRedisTemplate`)和`RedissonClient`。 示例: ```java @Service public class MyService { private final RedisTemplate<String, Object> redisTemplate; private final RedissonClient redissonClient; @Autowired public MyService(RedisTemplate<String, Object> redisTemplate, RedissonClient redissonClient) { this.redisTemplate = redisTemplate; this.redissonClient = redissonClient; } public void doSomething() { // 使用Spring Data Redis redisTemplate.opsForValue().set("key", "value"); // 使用Redisson RLock lock = redissonClient.getLock("lock"); lock.lock(); try { // 业务逻辑 } finally { lock.unlock(); } } } ``` 第四步:解决可能的Bean冲突 如果启动时出现`RedisConnectionFactory`等Bean的冲突,我们可以通过以下方式解决: 1. 在Spring Boot主类上添加`@EnableAutoConfiguration(exclude = {RedissonAutoConfiguration.class})`,然后手动配置RedissonClient?但是这样会禁用Redisson的自动配置。 2. 或者,我们可以通过配置让Spring Data Redis使用Lettuce(默认)连接工厂,而Redisson使用自己的连接工厂。由于Redisson的自动配置会创建一个`RedissonConnectionFactory`,而Spring Boot默认会使用LettuceConnectionFactory(如果我们没有其他配置)。但是,当我们同时存在两个`RedisConnectionFactory`时,Spring Boot会不知道注入哪个。因此,我们需要为Spring Data Redis指定一个主用的`RedisConnectionFactory`。 我们可以通过以下方式指定Spring Data Redis使用Lettuce连接工厂(默认就是使用Lettuce,所以只要我们不覆盖它即可),而Redisson使用它自己的连接工厂。但是,Redisson的自动配置默认会创建一个`RedissonConnectionFactory`,这可能会覆盖Spring Boot默认的Lettuce连接工厂。 为了避免覆盖,我们可以在Redisson的配置中设置不自动创建`RedissonConnectionFactory`?但是这样Spring Data Redis就无法使用Redisson作为连接工厂了(我们并不需要它使用,因为我们要两个共存)。 实际上,我们更希望: - Spring Data Redis使用Lettuce(通过spring.redis.*配置) - Redisson使用它自己的连接(通过redisson.config配置) 因此,我们需要确保Redisson的自动配置不会覆盖Spring Data Redis的`RedisConnectionFactory`。Redisson的自动配置(RedissonAutoConfiguration)在创建`RedissonConnectionFactory`时,会检查是否已经存在`RedisConnectionFactory`,如果存在,则不会创建。所以,我们可以通过让Spring Data Redis先创建`LettuceConnectionFactory`,然后Redisson自动配置会跳过创建`RedissonConnectionFactory`?但是这样RedissonClient就需要使用Lettuce连接工厂?这不符合我们的要求。 因此,我们需要两个独立的连接工厂。我们可以通过以下配置来实现: 1. 禁用Redisson的自动配置中的`RedissonConnectionFactory`创建,而是让RedissonClient使用它自己配置的连接(即通过`Config`对象创建的连接)。 实际上,Redisson的自动配置在创建`RedissonClient`时,是通过我们提供的配置文件(redisson.yml)来创建的,它并不依赖于Spring Data Redis的连接工厂。而`RedissonConnectionFactory`是Redisson提供的一个用于适配Spring Data Redis的`RedisConnectionFactory`实现,它内部使用RedissonClient。如果我们不需要让Spring Data Redis使用RedissonClient作为连接工厂,那么我们就不需要`RedissonConnectionFactory`。 所以,我们可以禁用`RedissonConnectionFactory`的自动创建。在Redisson的自动配置中,`RedissonConnectionFactory`是通过`RedissonAutoConfiguration`中的一个条件注解来创建的,我们可以在配置文件中关闭它: ```properties # 禁止Redisson自动配置创建RedissonConnectionFactory spring.redis.redisson.enabled=false ``` 但是,这个属性可能不起作用,因为Redisson的自动配置类中并没有提供这个开关。因此,我们可以通过排除`RedissonAutoConfiguration`中的`RedissonConnectionFactory`的创建?或者,我们直接不使用`redisson-spring-boot-starter`,而是手动配置RedissonClient。 另一种方案:我们不在Spring Data Redis中使用Redisson作为连接工厂,因此我们不需要`RedissonConnectionFactory`。我们可以通过排除`RedissonConnectionFactory`的Bean定义。但是,Redisson的自动配置会创建这个Bean,所以我们可以通过以下方式排除: ```java @SpringBootApplication(exclude = {RedissonAutoConfiguration.class}) ``` 但是这样整个Redisson的自动配置都被排除了,包括RedissonClient。所以,我们需要手动配置RedissonClient。 因此,我们可以选择不使用`redisson-spring-boot-starter`,而是直接引入`redisson`和`redisson-spring-data-24`,然后手动配置RedissonClient。 调整依赖: ```xml <!-- Spring Data Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.4.5</version> </dependency> <!-- Redisson核心库 --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.17.7</version> </dependency> <!-- Redisson Spring Data 24 适配器 --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-data-24</artifactId> <version>3.17.7</version> </dependency> ``` 然后,手动配置RedissonClient: ```java @Configuration public class RedissonConfig { @Bean(destroyMethod = "shutdown") public RedissonClient redisson() throws IOException { // 读取配置文件 Config config = Config.fromYAML(new ClassPathResource("redisson.yml").getInputStream()); return Redisson.create(config); } } ``` 这样,我们就有了两个Bean: - `LettuceConnectionFactory`(由Spring Boot自动配置创建,通过spring.redis.*配置) - `RedisTemplate`(由Spring Boot自动配置创建,使用LettuceConnectionFactory) - `RedissonClient`(由我们手动配置) 这样就不会冲突了。 总结步骤: 1. 修改pom.xml,去掉redisson-spring-boot-starter,改为直接依赖redissonredisson-spring-data-24。 2. 在resources目录下创建redisson.yml配置文件。 3. 创建RedissonConfig配置类,手动创建RedissonClient。 4. 在application.properties中配置Spring Data Redis(spring.redis.*)。 这样,我们就可以同时使用两个客户端了。 示例代码结构: 依赖: ```xml <dependencies> <!-- Spring Boot Starter Data Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.4.5</version> </dependency> <!-- Redisson --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.17.7</version> </dependency> <!-- Redisson Spring Data 24 adapter --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-data-24</artifactId> <version>3.17.7</version> </dependency> <!-- 其他依赖 --> </dependencies> ``` redisson.yml: ```yaml singleServerConfig: address: "redis://127.0.0.1:6379" password: null database: 0 connectionMinimumIdleSize: 5 connectionPoolSize: 10 ``` RedissonConfig.java: ```java import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import java.io.IOException; @Configuration public class RedissonConfig { @Bean(destroyMethod = "shutdown") public RedissonClient redisson() throws IOException { Config config = Config.fromYAML(new ClassPathResource("redisson.yml").getInputStream()); return Redisson.create(config); } } ``` 然后,在需要的地方注入`RedisTemplate`和`RedissonClient`即可。 注意:如果出现Bean定义冲突(比如有两个RedisConnectionFactory),我们可以通过@Primary来指定Spring Data Redis使用哪个连接工厂。但是,在我们的配置中,Spring Boot自动配置的RedisConnectionFactory(Lettuce)是默认的,而RedissonConnectionFactory并没有被创建,所以不会冲突。 这样,我们就实现了在Spring Boot 2.4.5项目中同时使用Spring Data Redis和Redisson。</think>在 Spring Boot 2.4.5 项目中同时集成 Redisson 和 Spring Data Redis 需要解决依赖冲突和配置隔离问题。以下是完整配置方案: ### 步骤 1:添加依赖并解决冲突(pom.xml) ```xml <dependencies> <!-- Spring Data Redis 基础依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.4.5</version> </dependency> <!-- Redisson 核心依赖 --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.17.7</version> <exclusions> <!-- 排除默认的 Spring Data 适配器 --> <exclusion> <groupId>org.redisson</groupId> <artifactId>redisson-spring-data-*</artifactId> </exclusion> </exclusions> </dependency> <!-- 添加 Spring Boot 2.4.x 专用适配器 --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-data-24</artifactId> <version>3.17.7</version> </dependency> </dependencies> ``` ### 步骤 2:配置文件(application.yml) ```yaml # Spring Data Redis 配置(用于常规操作) spring: redis: host: 127.0.0.1 port: 6379 database: 0 lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 # Redisson 独立配置(用于分布式锁等高级特性) redisson: config: classpath:redisson.yml ``` ### 步骤 3Redisson 专用配置(resources/redisson.yml) ```yaml singleServerConfig: address: "redis://127.0.0.1:6379" database: 0 connectionMinimumIdleSize: 5 connectionPoolSize: 10 idleConnectionTimeout: 10000 connectTimeout: 10000 timeout: 3000 ``` ### 步骤 4:解决 Bean 冲突(配置类) ```java @Configuration @EnableCaching public class RedisConfig { // Spring Data Redis 连接工厂 @Bean @Primary public LettuceConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory( new RedisStandaloneConfiguration("127.0.0.1", 6379)); } // Spring Data Redis 模板 @Bean @Primary public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory()); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } // Redisson 客户端(自动注入 redisson.yml 配置) @Bean public RedissonClient redissonClient() throws IOException { Config config = Config.fromYAML( new ClassPathResource("redisson.yml").getInputStream() ); return Redisson.create(config); } } ``` ### 步骤 5:在服务层同时使用 ```java @Service public class OrderService { // 注入 Spring Data Redis 模板 @Autowired private RedisTemplate<String, Object> redisTemplate; // 注入 Redisson 客户端 @Autowired private RedissonClient redissonClient; public void processOrder(String orderId) { // 使用 Spring Data Redis 存储数据 redisTemplate.opsForValue().set("order:"+orderId, "PROCESSING"); // 使用 Redisson 实现分布式锁 RLock lock = redissonClient.getLock("order_lock:"+orderId); try { if (lock.tryLock(10, 30, TimeUnit.SECONDS)) { // 临界区业务逻辑 processPayment(orderId); } } finally { lock.unlock(); } } } ``` ### 关键注意事项 1. **连接池分离**:为两个客户端配置独立连接池,避免资源竞争 2. **序列化统一**:确保 RedisTemplate 和 Redisson 使用相同的序列化协议 3. **Bean 优先级**:通过 `@Primary` 注解明确主用连接工厂 4. **配置覆盖**:在 `application.yml` 中添加: ```yaml spring: main: allow-bean-definition-overriding: true ``` ### 组件分工建议 | **操作类型** | **推荐组件** | **优势** | |--------------------|-------------------|-----------------------------| | 常规数据读写 | Spring Data Redis | 语法简洁,CRUD 操作高效 | | 分布式锁/限流 | Redisson | 内置看门狗机制,自动续期 | | 发布订阅 | 两者均可 | 根据消息可靠性要求选择 | | 地理空间运算 | Redisson | 提供 Geo 对象专用 API | > **最佳实践**:对于分布式锁场景,优先使用 Redisson 的 `RLock`,它通过[看门狗机制](https://redisson.org/)自动续期锁有效期,避免业务未完成时锁过期的问题[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值