Redis 作为高性能缓存、分布式锁、限流中间件,几乎是所有 SpringBoot 项目的标配。但在实际开发中,Redis 相关的坑**又多又隐蔽**:连接不上、序列化异常、缓存击穿、锁失效、乱码、数据丢失......很多问题线上才爆发,本地难以复现。
✅ 连接超时:Unable to connect to localhost:6379; ✅ 存入Redis乱码、key出现乱码\xAC\xED\x00\x05; ✅ 缓存查不到、自动消失、过期时间失效; ✅ 分布式锁不释放、锁失效、并发超卖; ✅ 反序列化失败:Could not read JSON; ✅ Redis集群/密码/SSL配置不生效; ✅ 缓存穿透、击穿、雪崩导致数据库打挂。
本篇把 SpringBoot 整合 Redis 的**9大高频报错&疑难场景**一次性讲透,每个问题都给:**报错原文 + 根因 + 可直接复制代码**,新手照着改就能用,老手也能查漏补缺,建议收藏!
一、基础依赖与标准配置(先避80%的坑)
1. 必引依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- 连接池(必须加,否则容易超时) --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
2. 推荐 yml 配置
spring: redis: host: localhost port: 6379 password: # 有密码就填,没有留空 database: 0 connect-timeout: 10000 timeout: 10000 # 连接池配置 lettuce: pool: max-active: 20 max-idle: 10 min-idle: 5 max-wait: -1
二、9大高频Redis报错+解决方案(按出现率排序)
场景1:连接超时 Connection refused / timed out
报错原文
Unable to connect to localhost:6379 Connection refused: no further information RedisConnectionException: Unable to connect
根因
-
Redis 未启动
-
bind 127.0.0.1 限制本地访问
-
protected-mode 保护模式开启
-
防火墙/安全组未放行 6379
-
密码错误或未配置密码
解决方案
-
启动 redis-server
-
redis.conf 改:
bind 0.0.0.0 -
关闭保护模式:
protected-mode no -
服务器放行 6379 端口
-
yml 正确配置 password
场景2:Redis key/value 乱码 \xAC\xED\x00\x05
现象
代码存正常,Redis 客户端看到一堆乱码,key 无法识别。
根因
使用默认 JdkSerializationRedisSerializer,二进制序列化。
解决方案:统一 RedisTemplate 序列化配置(复制即用)
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); // 序列化 Jackson2JsonRedisSerializer<Object> json = new Jackson2JsonRedisSerializer<>(Object.class); StringRedisSerializer string = new StringRedisSerializer(); // key string template.setKeySerializer(string); template.setHashKeySerializer(string); // value json template.setValueSerializer(json); template.setHashValueSerializer(json); template.afterPropertiesSet(); return template; } }
场景3:反序列化异常 Could not read JSON
报错
Could not read JSON: Cannot construct instance of... ClassNotFoundException
根因
-
实体类没有无参构造
-
类全限定名变了(包名/类名重构)
-
Redis 里还有旧格式数据
解决
-
实体加 **无参构造**
-
清理 Redis 旧缓存
-
使用泛型 + 指定类反序列化
场景4:设置过期时间不生效/立刻过期
现象
调用 expire 不生效,ttl 一直 -1;或设置完立刻消失。
根因
-
先 set 再 expire,网络异常导致两步不同时成功
-
key 被覆盖(重复 set)
解决
**一步 set 带过期**,原子操作:
redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
场景5:缓存穿透(查不存在的数据,直接打库)
解决
空值缓存 + 短期过期:
if (data == null) { redisTemplate.opsForValue().set(key, "", 60, TimeUnit.SECONDS); return null; }
场景6:缓存击穿(热点key过期,大量请求打库)
解决
互斥锁(Redis SETNX):
Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 5, TimeUnit.SECONDS); if (Boolean.TRUE.equals(lock)) { // 查库 → 写缓存 } else { Thread.sleep(100); return getFromCache(key); }
场景7:缓存雪崩(大量key同时过期)
解决
-
过期时间加随机偏移
-
多级缓存
-
集群高可用
int expire = 3600 + new Random().nextInt(1800);
场景8:分布式锁误删(业务超时,锁被别人删了)
根因
锁超时释放,别的线程加锁,本线程执行完删了别人的锁。
解决
锁存入**唯一标识**,只删自己的锁:
String requestId = UUID.randomUUID().toString(); Boolean lock = redisTemplate.opsForValue().setIfAbsent(key, requestId, 10, TimeUnit.SECONDS); // 删锁时判断 if (requestId.equals(redisTemplate.opsForValue().get(key))) { redisTemplate.delete(key); }
场景9:Redis 连接池耗尽 / 获取连接超时
根因
-
未使用连接池
-
max-active 太小
-
长事务/阻塞操作占用连接
解决
-
引入 commons-pool2
-
合理配置 pool 参数
-
禁止在 Redis 操作中做慢业务
三、Redis 万能排查步骤
-
redis-cli ping 看是否连通
-
检查 bind、protected-mode、密码
-
检查序列化配置(必配)
-
看 ttl 是否正常
-
看连接池是否满了
-
看是否出现锁竞争、key 覆盖
四、生产避坑指南
-
必须配置 RedisTemplate 序列化
-
必须用连接池(commons-pool2)
-
过期必须原子 set 带过期
-
分布式锁必须带唯一标识 + 超时
-
缓存一定要设防穿透、击穿
-
生产禁止 keys * 、flushdb
五、总结
SpringBoot+Redis 90% 的问题都来自:**连接配置、序列化、过期原子性、锁安全、缓存设计**。只要按本文配置和方案处理,线上绝大多数异常都能提前规避。
点赞+收藏🌟,以后遇到 Redis 问题直接翻这篇就够了!