Spring Boot项目中使用Redis存取List数据的最佳实践是什么?

作者:袖梨 2026-08-31

用 RedisTemplate + ListOperations 是最稳妥方案,但必须显式配置 GenericJackson2JsonRedisSerializer 序列化器,否则因默认 JDK 序列化导致取值为 null 或乱码;key 用 StringRedisSerializer,value 用 GenericJackson2JsonRedisSerializer,并注意 LocalDateTime 等类型需注册 JavaTimeModule。

直接结论:用 RedisTemplate + ListOperations 是最稳妥的方案,但必须显式配置序列化器,否则取出来的 List 会是乱码或 null

为什么默认的 RedisTemplate 取不到 List 数据?

Spring Boot 的 RedisTemplate 默认使用 JDK 序列化(JdkSerializationRedisSerializer),它把整个 List 对象当做一个整体序列化成二进制字节。你存进去的是 [{"id":"1","name":"a"},{"id":"2","name":"b"}],Redis 里实际存的是类似 aced00057372...(一长串十六进制) 的内容。

这时候如果用 range(key, 0, -1),Redis 返回的是一个个被序列化的 Object 元素,但反序列化失败——因为每个元素不是独立 JSON 字符串,而是嵌套在 List 容器里的完整对象字节流。

常见问题表现:

- range() 返回空 Listnull

- 控制台打印出类似 java.util.ArrayList@3a5e7876 的 toString 结果

- Redis CLI 中用 LRANGE myKey 0 -1 看到一堆不可读字符

怎么正确配置 RedisTemplate 支持 List 存取?

核心是让 key 和 value 都走 JSON 序列化,且确保每个 list 元素单独序列化(而不是整个 list 一起序列化)。推荐用 GenericJackson2JsonRedisSerializer

  1. 不要用 StringRedisTemplate —— 它只支持 String 类型,没法直接存 List<User>
  2. 不要用 RedisTemplate<String, String> 存对象 —— 会报 ClassCastException,因为反序列化时类型不匹配
  3. 必须指定 value 序列化器为 GenericJackson2JsonRedisSerializer,并传入具体泛型类型(如 User.class)或留空让它自动推断
  4. key 序列化器建议用 StringRedisSerializer,避免 key 也变成乱码

示例配置:

@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new GenericJackson2JsonRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());return template;}

leftPushAll 和 rightPushAll 的实际效果差异

这两个方法本质都是批量 push,区别仅在于插入顺序和语义:

  1. leftPushAll(key, list):把 list 中每个元素依次调用 LPUSH,结果是 list[0] 成为 Redis list 的 头元素(最左),list[n] 成为尾元素
  2. rightPushAll(key, list):对应 RPUSHlist[0] 成为 尾元素(最右),list[n] 成为头元素
  3. 性能上无明显差别,但如果你依赖「先进先出」或「后进先出」语义,选错会导致业务逻辑翻转

比如缓存用户最近操作日志,想按时间正序展示(最早 → 最新),就该用 rightPush 单个追加;若用 rightPushAll 批量写入一个已排序的 List<Log>,顺序会被反转。

取值时 range(key, 0, -1) 的边界陷阱

range 是安全的,但容易忽略两个细节:

  1. startend索引位置,不是元素个数。例如 range(key, 0, 9) 取前 10 个,range(key, 0, -1) 取全部
  2. 如果 key 不存在,range 返回空 List(不是 null),但如果你没判空直接调用 .size() 没问题;可一旦中间有元素是 null(比如序列化失败导致某次 push 写入了 null 字节),range 仍返回包含 null 元素的 List,后续遍历时可能触发 NullPointerException
  3. 大 List 场景下慎用 range(key, 0, -1):Redis 会把整个 list 加载进内存再切片,可能引发 OOM。应优先用分页方式(如 range(key, offset, offset+limit-1))或改用 lrange 命令配合游标

真正容易被忽略的是:当你用 GenericJackson2JsonRedisSerializerList<User>,但某个 User 字段含 Java 8 时间类型(如 LocalDateTime),而 Jackson 没配 JavaTimeModule,反序列化时会静默失败,对应位置返回 null —— 这种问题不会报错,只会让你在业务层莫名其妙拿到空对象。

相关文章

精彩推荐