Skip to content
作者:daily5am创建:-更新:-
字数:预计阅读: 分钟访问量:--

缓存一致性方案

AI生成声明: 本文档由AI辅助生成,旨在提供缓存一致性方案的完整指南。

缓存一致性是分布式系统中的经典问题,如何在保证性能的同时保证数据一致性是系统设计的难点。

核心问题

一致性级别

  1. 强一致性

    • 缓存和数据库完全一致
    • 性能最低
    • 实现复杂
  2. 最终一致性

    • 允许短暂不一致
    • 最终会一致
    • 性能较高
  3. 弱一致性

    • 不保证一致性
    • 性能最高
    • 可能读取到旧数据

一致性方案

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 ✓

结果:

  • 最终一致
  • 可以接受

最佳实践

  1. 根据业务选择策略

    • 强一致性:金融场景
    • 最终一致性:大多数场景
    • 弱一致性:统计场景
  2. 设置合理的过期时间

    • 平衡一致性和性能
    • 根据数据更新频率
  3. 使用版本号

    • 缓存带版本号
    • 比较版本决定是否更新
  4. 监控和告警

    • 监控缓存命中率
    • 检测数据不一致

常见面试题

  1. 如何保证缓存一致性?

    • 删除缓存策略
    • 更新缓存策略
    • 消息队列方案
  2. 最终一致性的实现?

    • 延迟双删
    • 消息队列
    • 定时刷新
  3. 强一致性的代价?

    • 性能下降
    • 实现复杂
    • 需要分布式事务