本地缓存怎么在分布式环境下保持一致性

本地缓存(如 Caffeine、Guava Cache)是节点本地的内存缓存,分布式场景下各节点缓存独立,易出现 "数据不一致"(如节点 A 更新了数据库,节点 B 的缓存还是旧数据),解决方案如下:

1、核心挑战

  • 数据孤岛:每个节点的缓存仅存储本地访问的数据,无法感知其他节点的更新;

  • 并发更新:多节点同时更新同一数据,可能导致 "部分节点缓存未更新";

  • 延迟问题:即使触发缓存更新,网络延迟也可能导致短期不一致。

2、解决方案

方案 1:事件通知(消息队列)
  • 核心原理:通过消息队列(如 Kafka、RabbitMQ)广播 "数据更新事件",所有节点监听事件,收到后删除 / 更新本地缓存。

  • 实现步骤

    • 节点 A 更新数据库后,向消息队列发送事件:{type: "delete", key: "user:1001"}

    • 所有节点(包括 A 自己)订阅该消息队列,收到事件后执行cache.delete("user:1001")

    • 后续节点查询 "user:1001" 时,缓存未命中,查数据库获取最新数据并写入缓存。

  • 优缺点

    • 优点:一致性较好(延迟低),实现灵活;

    • 缺点:依赖消息队列(需保证消息不丢失、不重复);

  • 适用场景:中高一致性要求、节点数适中的场景(如电商商品缓存)。

方案 2:发布 / 订阅模式(如 Redis Pub/Sub)
  • 核心原理:基于 Redis 的发布 / 订阅功能,更新节点发布 "缓存更新消息",其他节点订阅消息并更新缓存。

  • 实现步骤

    • 所有节点订阅 Redis 的 "cache_update" 频道;

    • 节点 A 更新数据库后,向 "cache_update" 频道发布消息:"delete:user:1001"

    • 其他节点收到消息后,解析出 key,执行cache.delete("user:1001")

  • 优缺点

    • 优点:轻量级(无需额外部署消息队列),实时性高;

    • 缺点:Redis Pub/Sub 不保证消息持久化(节点离线则丢失消息);

  • 适用场景:对一致性要求一般、可接受短期不一致的场景(如非核心业务缓存)。

方案 3:版本号控制
  • 核心原理:为每个缓存 key 关联一个 "版本号",查询时先对比版本号,不一致则更新缓存。

  • 实现步骤

    • 数据库表新增 "version" 字段(如 user 表的 version,每次更新 + 1);

    • 缓存存储 "数据 + 版本号":cache.set("user:1001", {data: ..., version: 3})

    • 节点 B 查询时,先查缓存的版本号(3),再查数据库的版本号(4);

    • 若缓存版本号 < 数据库版本号,删除缓存,重新查询数据库写入缓存。

  • 优缺点

    • 优点:不依赖外部组件,一致性可控;

    • 缺点:每次查询多一次数据库版本号检查(性能略有损耗);

  • 适用场景:对一致性要求较高、不希望依赖中间件的场景(如核心配置缓存)。

方案 4:定时全量同步
  • 核心原理:后台线程定期从数据库全量加载数据,覆盖本地缓存,保证最终一致性。

  • 实现步骤

    • 节点启动时,全量加载 "热门商品列表" 到本地缓存;

    • 后台线程每 5 分钟执行一次全量同步:List<Product> products = db.query("select * from product where is_hot=1")

    • 用新数据覆盖本地缓存:cache.putAll(products.stream().collect(toMap(p->p.getId(), p->p)))

  • 优缺点

    • 优点:实现简单,适合全量数据场景;

    • 缺点:一致性弱(同步周期内数据不一致),全量同步性能损耗大;

  • 适用场景:数据变化频率低、对一致性要求低的场景(如静态配置、热门商品列表)。

3、综合最佳实践

  • 核心数据:事件通知(Kafka)+ 版本号控制(双重保障);

  • 非核心数据:发布 / 订阅(Redis Pub/Sub);

  • 静态数据:定时全量同步 + 过期时间兜底;

  • 兜底策略:所有本地缓存设置合理的过期时间(如 5-10 分钟),即使一致性方案失效,过期后也会自动更新。

相关推荐
superlls3 小时前
(定时任务)接上篇:定时任务的分布式执行与分布式锁使用场景
java·分布式·后端
回家路上绕了弯4 小时前
高并发后台系统设计要点:从流量削峰到低延迟的实战指南
分布式·后端
Lansonli5 小时前
大数据Spark(六十九):Transformation转换算子intersection和subtract使用案例
大数据·分布式·spark
太阳伞下的阿呆5 小时前
kafka与zero-copy
分布式·kafka
心态特好5 小时前
从缓存到分库分表:MySQL 高并发海量数据解决方案全解析
数据库·mysql·缓存
没有bug.的程序员6 小时前
云原生与分布式架构的完美融合:从理论到生产实践
java·分布式·微服务·云原生·架构
JanelSirry6 小时前
分布式和微服务的区别是什么?
分布式·微服务·架构
在云上(oncloudai)9 小时前
Amazon ElastiCache 全解析:打造高性能的智能缓存架构
缓存·架构
Jabes.yang9 小时前
Java面试大作战:从缓存技术到音视频场景的探讨
java·spring boot·redis·缓存·kafka·spring security·oauth2