Redis-08 SpringBoot集成Redis常见问题

发布于:2025-09-02 ⋅ 阅读:(17) ⋅ 点赞:(0)

        SpringBoot集成Redis的教程网上很多,总体来说就是三个步骤:添加依赖、修改配置文件、自定义Redis配置类(自定义序列化器),具体步骤可自行搜索,本文主要解惑集成中的常见疑问。

1,选择什么依赖?

        SpringBoot集成Redis共有三种依赖可选:spring-boot-starter-data-redis,spring-data-redis,redisson-spring-boot-start。先说结果:

        优先选择 ​​spring-boot-starter-data-redis​​,快速实现缓存功能,避免过度设计。

        高并发或分布式系统​​,直接使用 ​​redisson-spring-boot-starter​​,其内置的分布式锁、限流器可显著降低复杂度。

    1),spring-boot-starter-data-redis(官方推荐)

        定位​​:Spring Boot官方提供的开箱即用方案,封装了Spring Data Redis的核心功能。
​​        底层客户端​​:默认使用Lettuce(基于Netty的非阻塞I/O),也可切换为Jedis(传统阻塞式I/O)。
​​        核心优势​​:
​​                自动化配置​​:仅需配置spring.redis.host/port等参数即可自动初始化连接池和RedisTemplate。
​​                统一操作接口​​:提供RedisTemplate和StringRedisTemplate,支持字符串、哈希、列表等数据结构操作。
​​                与Spring生态无缝集成​​:支持Spring Cache抽象、事务管理和Repository模式。
​​        局限性​​:
                高级功能(如分布式锁、限流)需自行实现(如Lua脚本或第三方库)。
                默认序列化方式为JDK序列化(可读性差),需手动配置JSON序列化。
        适用场景:简单缓存、基础数据操作
        性能:中等(依赖Lettuce/Jedis)
        高级功能:需自行实现

    2),spring-data-redis

        非SpringBoot项目(如Springmvc,Spring)引入的依赖,不包含客户端、连接池,需手动配置连接工厂。spring-boot-starter-data-redis​​已包含​​ spring-data-redis。

    3),redisson-spring-boot-start(分布式redis依赖)

        定位​​:Redisson提供的增强方案,支持分布式对象和高级分布式功能。
        核心优势​​:
                开箱即用的分布式工具​​:内置分布式锁(支持自动续期)、信号量、限流器(RRateLimiter)、延迟队列等。
        高性能本地缓存​​:通过RLocalCachedMap减少网络IO,缓存命中率提升显著(压测可达百万级QPS)。
        异步与响应式支持​​:基于Netty的非阻塞通信,支持RBatch批量操作提升吞吐量。
        局限性​​:
                学习成本高,需理解分布式对象(如RMap、RBucket)的API设计。
                部分简单操作(如基础字符串存取)代码量略多于RedisTemplate。
        适用场景:分布式锁、限流、高并发系统
        性能:高于spring-boot-starter-data-redis,基于Netty异步+本地缓存优化
        高级功能:内置分布式工具

2,多模块怎么将Redis配置文件和配置类独立出来,避免各模块重复工作?

  • 微服务系统可将做配置中心;
  • 非微服务系统可设置一个base-module模块,在需要使用redis的模块的pom文件中引入该模块的依赖。

3,为什么要配置Redis连接池,其作用是什么,有哪些连接池可选?

        Redis连接池(Pool)是指客户端连接池,是为了管理客户端(如Jedis或Lettuce)与Redis服务器之间的网络连接而存在的。

        Redis服务端可简单理解成单线程应用(实际非单线程,参考文章Redis-02 单线程与高性能-CSDN博客),无连接池一说。

        Redis客户端(即自己的程序,是多线程的)通常是Springboot应用,是多线程的web服务,可同时处理上万用户请求,可能产生并发的Redis操作请求,配置客户端连接池,可在客户端请求Redis时,减少客户端连接Redis服务器带来的创建、销毁等开销(如建立tcp连接、tcp断开、认证等)。

4,RedisTemplate和StringRedisTemplate是什么,有什么区别,生产环境一般怎么使用?

        科普:

        它们都是Spring Data Redis提供的核心工具类,用于在Java应用中与Redis服务器进行交互,封装了底层的连接管理、序列化/反序列化等复杂操作,让你可以用面向对象的方式操作Redis。

        区别:

        核心的区别是默认的序列化器不同。序列化器决定了Java对象如何转换成二进制数据存入Redis,以及如何从二进制数据变回Java对象。可以把 StringRedisTemplate 看作是 RedisTemplate<String, String> 的一个特例,它强制要求键和值都是字符串,并使用字符串序列化器。

RedisTemplate与StringRedisTemplate对比
StringRedisTeplate RedisTeplate
key序列化器 StringRedisSerializer JdkSerializationRedisSerializer
value序列化器 StringRedisSerializer JdkSerializationRedisSerializer
存储结果 可读的字符串。set("name", "Alice") 在Redis中存的就是 "Alice" 不可读的JDK序列化字节码。存进去是乱码,如 \xac\xed\x00\x05t\x00\x04name
使用场景 处理字符串和文本结构的数据 处理复杂的Java对象(但需要配置序列化器)

        生产环境怎么使用:

        优先使用 StringRedisTemplate 来处理字符串键值对。

        对于需要存储复杂Java对象的场景,会配置一个专门化的 RedisTemplate(例如 RedisTemplate<String, Object>),并将其值的序列化器改为JSON序列化器(如 Jackson2JsonRedisSerializer 或 GenericJackson2JsonRedisSerializer)。

        绝对要避免使用默认配置(JDK序列化)的 RedisTemplate!

5,生产环境怎么设置RedisTemplate的序列化器?

        以常用的GenericJackson2JsonRedisSerializer序列化器为例,生产环境各种配置方式如下:

        1),简单快速使用设置

@Configuration
public class RedisTemplateConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        // 设置Key和HashKey的序列化器为StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());

        // 设置Value和HashValue的序列化器为GenericJackson2JsonRedisSerializer (JSON格式)
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);

        template.afterPropertiesSet();
        return template;
    }

}

        上述序列化器设置完成即可使用Redis,但是有个弊端,假如存放的是对象,如User(id, name, age, sex, position),在Redis中是Json数据格式,web端获取User时,返回的是LinkedHashMap<String, Object>,无法直接强转成User对象。同样,如果存放的是List<User>,在Redis中是Json数组,返回则是List<LinkedHashMap>。

        2),优化序列化设置

@Configuration
public class RedisTemplateConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        // 设置Key和HashKey的序列化器为StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());

        // 设置Value和HashValue的序列化器为GenericJackson2JsonRedisSerializer (JSON格式),并配置返回序列化类型

        // 创建 ObjectMapper 并配置多态类型支持
        /**
         * 作用:
         * 这行代码配置 ObjectMapper 在序列化 JSON 时,自动嵌入类的类型信息。
         * 这样在反序列化时,Jackson 就能根据这些信息准确地还原成原始对象类型,而不是简单的 LinkedHashMap。
         */
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance, // 非严格验证,更严谨的使用方式是将类型验证器LaissezFaireSubTypeValidator替换为TypeValidator
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY
        );
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);

        template.afterPropertiesSet();
        return template;
    }

}

        如上述代码,多添加了ObjectMapper,并激活了如下几个配置:

  • LaissezFaireSubTypeValidator.instance:配置了对象的类型验证器,允许反序列化任何类型(有安全风险,但操作简单方便开发)。
  • ObjectMapper.DefaultTyping.NON_FINAL:指定哪些类型需要包含类型信息。NON_FINAL:所有非final类都需要包含类型信息。可选项还有:NON_CONCRETE_AND_ARRAYS(非具体类型和数组), EVERYTHING(所有类型)
  • JsonTypeInfo.As.PROPERTY:指定类型在Redis中如何潜入到JSON里。PROPERTY:作为单独属性添加到JSON中。可选项还有:WRAPPER_ARRAY(包装为数组),WRAPPER_OBJECT(包装为对象)。

        添加如上反序列化配置后,假如存放的是List<User>,则Redis中存储格式如下:

[
  "java.util.ArrayList",
  [
    [
      "com.example.User",
      {"id":1,"name":"张三"}
    ],
    [
      "com.example.User", 
      {"id":2,"name":"李四"}
    ]
  ]
]

        客户端获取数据时,反序列化就知道该对象是List,内部对象是User,这样结果就可以直接强转。

        3),反序列化类型验证器

package com.mango.basedata.common;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisTemplateConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        // 设置Key和HashKey的序列化器为StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());

        // 设置Value和HashValue的序列化器为GenericJackson2JsonRedisSerializer (JSON格式),并配置返回序列化类型

        // 创建 ObjectMapper 并配置多态类型支持
        /**
         * 作用:
         * 这行代码配置 ObjectMapper 在序列化 JSON 时,自动嵌入类的类型信息。
         * 这样在反序列化时,Jackson 就能根据这些信息准确地还原成原始对象类型,而不是简单的 LinkedHashMap。
         */
        ObjectMapper objectMapper = new ObjectMapper();

        // 创建更安全的类型验证器(白名单机制)
        BasicPolymorphicTypeValidator typeValidator = BasicPolymorphicTypeValidator.builder()
                .allowIfSubType("com.example.models.entity")
                .allowIfSubType("java.util.ArrayList")
                .allowIfSubType("java.util.HashMap")
                .build();
        
        objectMapper.activateDefaultTyping(
                typeValidator,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY
        );
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);

        template.afterPropertiesSet();
        return template;
    }
    
}


网站公告

今日签到

点亮在社区的每一天
去签到