沉默是金,总会发光
大家好,我是沉默
缓存不是“快就完事”,它关系到可用性、数据一致性与运维成本。
错误的缓存策略,会把你从 200ms 拉升到 2s,甚至引发雪崩、脏数据风暴。
本文把多级缓存流程策略、项目实践、常见坑与面试样例都给你,适合直接复制到项目中执行。
**-**01-
选型决策
常用缓存对比表:
| 类型 | Spring Cache(抽象) | Caffeine | Ehcache | Redis | Guava(已淘汰) |
|---|---|---|---|---|---|
| 特性 | 抽象层 | 本地(JVM) | 本地/持久化 | 分布式 | 本地(旧) |
| 性能 | 取决于实现 | 极高(W-TinyLFU) | 中高(支持磁盘) | 高(网络延迟依赖) | 中(LRU) |
| 分布式支持 | 无(接口) | 无 | 有(复杂) | 原生 | 无 |
| 持久化 | 无 | 无 | 支持磁盘 | 支持 RDB/AOF | 无 |
| 适用场景 | 注解化接入 | 单机高并发热点 | 需要重启恢复场景 | 跨服务共享/高并发读写 | 维护旧项目 |
- 02-
多级缓存请求流程
微服务架构一般用 Caffeine、Redis, 两者结合:
- 03-
实战案例代码
- Caffeine 配置(Spring Boot)
@Configuration@EnableCachingpublic class CaffeineConfig { @Bean public CacheManager cacheManager() { CaffeineCacheManager mgr = new CaffeineCacheManager(); mgr.setCaffeine(Caffeine.newBuilder() .initialCapacity(1000) .maximumSize(10_000) .expireAfterWrite(5, TimeUnit.MINUTES) .recordStats()); return mgr; }}
2. Redis Cache 配置(Spring Boot)
spring: redis: host: redis-server port: 6379 lettuce: pool: max-active: 8
@Configuration@EnableCachingpublic class RedisCacheConfig { @Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisCacheConfiguration cfg = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofHours(1)) .disableCachingNullValues() .serializeValuesWith(RedisSerializationContext.SerializationPair .fromSerializer(new GenericJackson2JsonRedisSerializer())); return RedisCacheManager.builder(factory).cacheDefaults(cfg).build(); }}
3. 多级缓存读取示例(本地 + Redis + DB)
@Servicepublic class MultiLevelCacheService { @Autowired private CacheManager caffeineCacheManager; @Autowired private CacheManager redisCacheManager; @Autowired private ProductRepository productRepository; public Product getProduct(Long id) { Product p = caffeineCacheManager.getCache("products").get(id, Product.class); if (p != null) return p; p = redisCacheManager.getCache("products").get(id, Product.class); if (p != null) { caffeineCacheManager.getCache("products").put(id, p); return p; } p = productRepository.findById(id).orElse(null); if (p != null) { redisCacheManager.getCache("products").put(id, p); caffeineCacheManager.getCache("products").put(id, p); } return p; }}
4. 延时双删(更新一致性推荐做法)
@Transactionalpublic void updateProduct(Product product) { // 第一次删除缓存 redisTemplate.delete("product:" + product.getId()); // 更新 DB productDao.update(product); // 异步延时再删除一次 CompletableFuture.runAsync(() -> { try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } redisTemplate.delete("product:" + product.getId()); });}
**-****04-**总结
常见坑(必看)
- 禁止:先删缓存再更新 DB(会产生脏数据)——用延时双删或事务消息。
- 不要:无限制缓存时间(必须 TTL)。
- 小心:
allEntries=true全量清理会导致瞬间穿透。 - 秒杀不要用悲观锁:悲观锁严重影响吞吐,优先用原子 SQL 或 Redis Lua。
缓存一致性策略速览(面试必背)
| 策略 | 实现复杂度 | 一致性 | 适用场景 |
|---|---|---|---|
| 写时更新(Write-Through) | 低 | 高 | 金融类强一致场景 |
| 写后更新(Write-Behind) | 中 | 中 | 异步高吞吐场景 |
| 延时双删(Delayed Double Delete) | 中 | 高 | 秒杀 / 高并发更新 |
| 版本号校验(Cache + Version) | 中 | 高 | 精细覆盖控制(配置/用户设置) |
| 事务消息(MQ) | 高 | 高(最终) | 分布式微服务架构 |
生产级最佳实践(落地清单)
-
优先画决策树:先回答“读写比、是否跨服务、是否强一致”三题再选方案。
-
设置 TTL(必做) :防止雪崩,热点可短 TTL + 后台刷新。
-
防穿透:布隆过滤器 + 空值缓存(短 TTL)。
-
同步本地缓存:Redis Pub/Sub 或消息队列通知各实例清理本地缓存。
-
监控报警:命中率、缓存加载时延、Redis 内存、DB QPS。阈值示例:命中率 < 0.7 警告。
-
降级策略:缓存不可用时自动查询 DB 并限流降级(避免级联故障)。
-
并发控制:热点数据 get 时可用 lock.tryLock + 双重检查避免击穿。
最后
单体/单机热点:用 Caffeine(JVM 本地,超低延迟)。
跨服务/分布式共享:用 Redis(分布式、持久化、HA)。
希望兼顾延迟与一致性:选 Caffeine + Redis(多级缓存 / Cache-Aside)。
旧项目兼容:Guava 可维,但新项目弃用,改用 Caffeine。
**-****05-**粉丝福利
我这里创建一个程序员成长&副业交流群,
和一群志同道合的小伙伴,一起聚焦自身发展,
可以聊:
技术成长与职业规划,分享路线图、面试经验和效率工具,
探讨多种副业变现路径,从写作课程到私活接单,
主题活动、打卡挑战和项目组队,让志同道合的伙伴互帮互助、共同进步。
如果你对这个特别的群,感兴趣的,
可以加一下, 微信通过后会拉你入群,
但是任何人在群里打任何广告,都会被我T掉。