缓存一致性方案
AI生成声明: 本文档由AI辅助生成,旨在提供缓存一致性方案的完整指南。
缓存一致性是分布式系统中的经典问题,如何在保证性能的同时保证数据一致性是系统设计的难点。
核心问题
一致性级别
强一致性
- 缓存和数据库完全一致
- 性能最低
- 实现复杂
最终一致性
- 允许短暂不一致
- 最终会一致
- 性能较高
弱一致性
- 不保证一致性
- 性能最高
- 可能读取到旧数据
一致性方案
1. 删除缓存策略
先更新数据库,后删除缓存:
java
// 更新数据库
database.update(key, value);
// 删除缓存
cache.delete(key);优点:
- 实现简单
- 性能较好
缺点:
- 可能出现短暂不一致
2. 更新缓存策略
先更新数据库,后更新缓存:
java
// 更新数据库
database.update(key, value);
// 更新缓存
cache.update(key, value);优点:
- 缓存始终有效
- 减少缓存穿透
缺点:
- 可能更新非热点数据
- 浪费内存
3. 延迟双删策略
java
// 第一次删除
cache.delete(key);
// 更新数据库
database.update(key, value);
// 延迟删除
Thread.sleep(500);
cache.delete(key);目的:
- 解决并发问题
- 保证最终一致性
4. 消息队列方案
java
// 更新数据库
database.update(key, value);
// 发送消息
messageQueue.send(new CacheUpdateMessage(key));消费者:
java
// 消费消息
public void consume(CacheUpdateMessage message) {
cache.delete(message.getKey());
}优点:
- 解耦
- 保证最终一致性
- 支持重试
并发场景处理
场景1:读写并发
线程A: 更新数据库 (value=1)
线程B: 读取缓存(未命中)
线程B: 读取数据库(value=1)
线程A: 删除缓存
线程B: 写入缓存(value=1)
结果: 缓存是旧值解决:
- 使用分布式锁
- 或接受短暂不一致
场景2:写写并发
线程A: 更新数据库 (value=1)
线程B: 更新数据库 (value=2)
线程B: 删除缓存
线程A: 删除缓存
结果: 缓存为空,下次读取到value=2 ✓结果:
- 最终一致
- 可以接受
最佳实践
根据业务选择策略
- 强一致性:金融场景
- 最终一致性:大多数场景
- 弱一致性:统计场景
设置合理的过期时间
- 平衡一致性和性能
- 根据数据更新频率
使用版本号
- 缓存带版本号
- 比较版本决定是否更新
监控和告警
- 监控缓存命中率
- 检测数据不一致
常见面试题
如何保证缓存一致性?
- 删除缓存策略
- 更新缓存策略
- 消息队列方案
最终一致性的实现?
- 延迟双删
- 消息队列
- 定时刷新
强一致性的代价?
- 性能下降
- 实现复杂
- 需要分布式事务