分布式缓存 java_分布式缓存(Redis)
- 云服务器
- 2026-08-27
- 5
Java分布式缓存,Redis是绝大多数团队绕不开的标准答案,但真把它用好、用稳、用出性能,靠的是对缓存穿透、雪崩、一致性、集群架构和底层部署的深度理解,而不是单纯地引入一个依赖。
我做了九年Java后端,从单体应用到支撑百万日活的微服务集群,Redis从未缺席,期间踩过的坑、重构过的架构、在线上熬过的夜,比看过的源码多得多,这篇内容不讲虚的,把分布式缓存(Redis)从设计到落地的关键点,掰开揉碎讲清楚。
分布式缓存的本质:把复杂的计算,推给更快的存储
为什么需要分布式缓存?一句话:数据库的IO能力是瓶颈,内存的IO能力是天花板。 当业务请求量上来,你不可能无限加数据库的硬盘或者连接数,那样成本太高且收益递减,缓存的存在,就是为了让热点数据停留在更快的介质上,让绝大多数请求在内存层面直接返回。
Java世界里,本地缓存(如Caffeine)和分布式缓存(如Redis)是两种路线,本地缓存快但无法跨实例共享,分布式缓存慢一点(但比数据库快几个数量级)却能支撑整个集群的访问,真正的生产环境,两者常搭配使用:本地缓存做一级,Redis做二级,数据库做兜底。
这套逻辑在很多高并发场景的行业白皮书(如阿里巴巴《Java开发手册》的缓存规范章节)里都被反复强调——先查本地,再查Redis,最后落库,能挡掉不少压力。
Java程序员使用Redis必须跨越的四道坎
任何分布式缓存方案,只要上了规模,必然遇到四件事:数据一致性、缓存穿透、缓存雪崩、集群容错,这是所有缓存框架、中间件、乃至云厂商的SDK都在尝试解决的问题,没有银弹,但有不踩坑的路径。
第一道坎:手动挡是关键,自动挡是幻觉
很多框架(比如Spring Cache)提供了@Cacheable注解,号称一行代码搞定缓存,但我强烈建议,核心链路里的缓存逻辑一定要手动控制,原因是注解式的缓存粒度太粗,你无法精确控制缓存过期的时间点、无法精细地处理缓存和DB之间的事务一致性,一旦触发坑,排查成本极高。
推荐做法:使用Spring Boot集成Redis时,手动封装一个RedisService,提供setWithExpire、getObject、deleteByPattern等方法,操作路径如下:
- 引入spring-boot-starter-data-redis依赖。
- 配置连接工厂(RedisConnectionFactory),生产环境务必使用Lettuce连接池并配置合理的maxTotal和maxIdle。
- 手动编写缓存查询逻辑:先查缓存,命中则返回,未命中则查DB,再回填缓存并设置随机过期时间。
这套手动挡模式虽然代码量多,但每一个环节都在你的掌控之中,出了问题能快速定位。
第二道坎:缓存穿透,必须布隆过滤器兜底
缓存穿透指查询一个根本不存在的数据,由于缓存没有这个key,请求会直接打到数据库,恶意攻破时数据库会被击穿。
解决方案并不是简单地缓存空值(但这确实是第一道防线),更稳妥的是引入布隆过滤器(Bloom Filter),在查询缓存之前,先用布隆过滤器判断这个key是否可能存在,如果过滤器说不存在,直接返回。
操作上,你可以使用Redisson的RBloomFilter,也可以在应用启动时预加载所有有效ID到过滤器里,核心配置参数有两个:预期元素数量和误判率(通常设置为万分之一),需要留意的是,布隆过滤器不支持删除操作,这也是一个小坑。
第三道坎:缓存雪崩和击穿,加锁加随机过期
缓存雪崩是大量key在同一时间失效,或者Redis实例直接宕机,导致流量全部打向数据库,缓存击穿是某一个热点的key失效,大量并发线程同时去数据库查询。
解决思路:
- 过期时间加随机值,比如基础过期时间设置为300秒,再增加一个0到60秒的随机数。
- 热点key加互斥锁(分布式锁),当缓存未命中时,先尝试获取分布式锁(Redisson的RLock),只有拿到锁的线程才去数据库查询并回填缓存,其它线程等待片刻后重新读取缓存。
- Redis高可用保障,这需要底层设施做支撑,比如使用哨兵模式或者Cluster集群模式。
第四道坎:缓存和数据库的一致性,延迟双删是底线
最经典的问题:更新数据库后,如何删除缓存?直接删吗?如果先更新DB再删缓存,中间有一瞬间缓存是旧数据,如果先删缓存再更新DB,更新DB失败则缓存为空,流量打到DB。
最稳妥且被广泛应用的是延迟双删策略:
- 先删除缓存。
- 更新数据库。
- 休眠一小段时间(比如500毫秒)。
- 再次删除缓存。
这个休眠时间要大于一次读请求把脏数据写回缓存的时间,配合Binlog监听(Canal)异步删除缓存也是分布式场景下的一个成熟方案,从实际生产参数看,延迟双删配合分布式锁,能保证最终一致性。
Redis集群怎么选:Redis Cluster 还是 主从哨兵?
选型决定上限,如果你的数据量不大(内存占用不超过单机16G),主从+哨兵完全够用,Redis Sentinel能自动完成故障转移,主库挂了哨兵会自动将从库提升为主库,Java客户端的MasterSlaveConnectionFactory也能自动感知拓扑变化。
如果你的数据量继续膨胀,甚至达到几十G上百G,必须采用 Redis Cluster,Cluster将数据自动分片到16384个槽位中,理论上支持横向扩展。

对比表格:
| 对比维度 | 主从哨兵(Sentinel) | Redis Cluster |
|---|---|---|
| 数据容量 | 受单机内存限制 |
可水平扩展至多机 |
| 故障转移 | 秒级自动切换 | 秒级自动迁移槽位 |
| 客户端支持 | 需支持拓扑刷新 | 需支持Cluster协议 |
| 运维复杂度 | 相对简单 | 相对复杂 |
| 典型适用场景 | 数据量可控、读多写少 | 数据量大、高并发写入 |

