java本地缓存需作为多级缓存第一层,选用caffeine支持集合操作与自动驱逐;缓存值可为list/set/map等不可变集合;通过事件驱动+ttl实现软一致性;须控制内存占用并监控命中率。

在分布式系统中,Java 本地缓存本身不具备跨节点共享能力,因此“基于集合的本地高效缓存”不能单独承担数据一致性职责,而应作为多级缓存的第一层——即每个服务实例内部维护一个高性能、线程安全、支持集合操作(如批量读写、contains、size、迭代等)的内存缓存。关键在于:它要快、轻量、可控,并与分布式缓存协同工作。
选型:优先用 Caffeine 而非 ConcurrentHashMap
Caffeine 是当前 Java 生态中事实标准的本地缓存库,比裸用 ConcurrentHashMap 更适合生产级集合类缓存场景:
- 原生支持 LRU/LFU 驱逐策略,避免手动管理容量导致 OOM
- 内置基于权重(weight)或数量(maximumSize)的自动淘汰,可为集合元素设统一权重或按实际内存估算
- 提供
asMap()接口直接暴露 ConcurrentMap 视图,天然支持containsKey、keySet()、values()等集合操作 - 支持异步刷新、统计监控、过期时间(expireAfterWrite / expireAfterAccess),便于调试和容量治理
设计集合语义:缓存值类型需适配业务集合需求
本地缓存不是只能存单个对象;它完全可以缓存 List、Set、Map 等集合类型,只要序列化/反序列化逻辑明确、线程安全:
- 若缓存「用户权限集合」,可定义
Cache<long set>></long>,key 是 userId,value 是权限字符串集合 - 若缓存「商品 SKU 列表」,可用
Cache<string list>></string>,key 是 categoryCode,value 是该类目下热门 SKU 列表 - 避免缓存可变集合(如未包装的 ArrayList);推荐使用
Collections.unmodifiableSet()或 Guava 的ImmutableSet包装后存入,防止外部误修改破坏缓存一致性
规避分布式场景下的本地缓存不一致问题
本地缓存天然不同步,必须配合事件机制实现“软一致性”:
- 写操作走「先改 DB → 再删 Redis → 发布本地失效消息」流程,例如用 Redis Pub/Sub 或 Kafka 广播
cache-invalidate:user:123 - 各节点监听消息,收到后调用
caffeineCache.invalidate(key)清除对应本地条目 - 对高频小集合(如配置项、状态码映射),可设置较短 TTL(如 30 秒),靠自然过期兜底,降低消息依赖
- 不建议用“主动同步全量集合”方式更新本地缓存,开销大且易丢包;只做 key 级别失效 + 懒加载回填更可靠
性能与内存控制要点
集合类缓存容易隐式放大内存占用,需针对性约束:
- 启用
maximumSize(1000)或maximumWeight(10_000_000),并为每个集合 value 实现Weigher接口(例如返回list.size() * 256估算字节数) - 禁用
refreshAfterWrite对集合类型——因为刷新会触发重建整个集合,可能引发重复 DB 查询或锁竞争 - 开启 Caffeine 的
recordStats(),通过cache.stats().hitRate()监控命中率,低于 85% 时需检查集合粒度是否过大(比如把“全站商品列表”缓成一个 key,不如按类目拆分)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











