redis stream 是 spring boot 中唯一能真正保证消息不丢、可重试、可回溯的 redis 消息队列方案;list 和 pub/sub 因缺乏 ack 机制,生产环境慎用。

Redis Stream 是目前 Spring Boot 项目中用 Redis 做消息队列时,唯一能真正保证消息不丢、可重试、可回溯的方案;List 和 Pub/Sub 都不具备 ACK 机制,生产环境慎用。
为什么 List 实现的消息队列容易丢消息
List 本身没有消费确认概念,靠 BRPOP 或 RPOP 拿到消息后,一旦消费者崩溃或处理失败,消息就永远消失了。常见错误写法是:
- 用
while(true)轮询redisTemplate.opsForList().rightPop("queue"),没做异常兜底 - 消息处理逻辑里没加 try-catch,出异常直接中断,没把消息塞回队列或落库
- 没配 Redis 持久化(
appendonly yes),实例重启后整个 List 清空
即使加了 BLMOVE 做“处理中队列”,也得自己维护状态、超时转移、死信判断——这些本该由消息系统内置的能力,全得手写。
Stream 的 ACK 机制怎么防止消息丢失
Stream 的可靠性核心在消费组(Consumer Group)+ Pending 列表(PENDING)+ XACK。Spring Data Redis 封装了关键操作:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 消费者调用
opsForStream().read(Consumer.from("group", "consumer1"), StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)), StreamOffset.fromStart("stream")),会自动将消息加入该消费者的PENDING列表 - 处理成功后必须显式调用
opsForStream().acknowledge("stream", "group", recordId),否则消息一直留在PENDING中 - 消费者宕机后,其他成员可通过
opsForStream().pending("stream", "group")查出未 ACK 消息,再用XCLAIM(对应opsForStream().claim(...))抢过来继续处理
注意:opsForStream().read() 默认不自动 ACK,必须手动 acknowledge();漏掉这步,等于没消费。
Spring Boot 配置 Redis Stream 的关键避坑点
不是加上依赖就能用好 Stream,几个配置细节直接影响可靠性:
-
spring.redis.lettuce.pool.max-active必须 ≥ 消费者线程数,否则连接池耗尽,read()请求被阻塞或超时 -
RedisTemplate的 value 序列化器必须支持类型信息,推荐Jackson2JsonRedisSerializer配合ObjectMapper.activateDefaultTyping(...),否则反序列化失败导致消息卡在 Pending - 创建消费组前,先确保 Stream 已存在,否则
createGroup()会失败;可用opsForStream().add()写一条空消息触发创建 - 不要用
StringRedisTemplate处理 Stream,它不支持泛型 Record 结构,read()返回值无法正确解析
最易被忽略的是:Stream 消息 ID 默认为 timestamp-millis-sequence,但如果你手动指定 ID(如 "0-1"),会导致顺序错乱、范围查询失效,除非你真需要全局有序且能控制生成逻辑。










