如何保证缓存数据的一致性:数据库和缓存数据一致性,本地缓存和Redis缓存怎么保证数据一致性

多级缓存,本地缓存和Redis缓存怎么保证数据一致性?

设置本地缓存短时间内失效

设置本地缓存短时间内失效,短的存活周期,保证了数据的时效性比较高,当数据失效之后,再次访问数据就会拉取新的数据了,这样能尽可能的保证数据的一致性。

它的特点是:代码实现简单,不需要写多余的代码;缺点是,效果不是很明显,不适合高并发的系统。

本地缓存自动更新功能

使用本地缓存框架的自动更新功能,例如 Caffeine 中的 refresh 功能来自动刷新缓存,这样就可以设置很短的时间来更新最新的数据,从而也能尽可能的保证数据的一致性。

更新策略 refreshAfterWrite expireAfterWrite
触发时机 访问时延迟刷新 到期立即失效
数据可用性 始终有数据(旧或新) 可能短暂无数据
性能影响 异步无阻塞 同步加载可能阻塞

refreshAfterWrite执行过过程

复制代码
写入数据A(time=0s) 
→ 5秒后(time=5s)数据仍在缓存中 
→ 第6秒访问时触发异步刷新 
→ 立即返回旧数据A 
→ 后台加载新数据A' 
→ 加载完成后替换缓存中的A

refreshAfterWrite的底层实现原理‌是采用‌惰性触发+异步加载‌的机制。具体实现可分为先步骤:


一、时间标记与条件检查

  1. 写入时间记录

    每个缓存项会记录最后一次写入时间戳,作为刷新时间计算的基准。

  2. 访问时阈值判断

    当访问缓存时,比较当前时间与写入时间+refresh间隔

    • 若未达到阈值:直接返回现有值
    • 若达到阈值:触发异步刷新流程

二、异步刷新控制

  1. 非阻塞式加载

    通过后台线程池(如ForkJoinPool)异步执行刷新任务,避免阻塞用户请求线程。

  2. 单线程防抖

    对同一key的并发刷新请求会合并,确保只有一个加载操作执行(通过细粒度锁或CAS控制)。

  3. 新旧数据隔离( 采用类似ConcurrentHashMap的分段锁设计**)**‌

    • 刷新过程中继续返回旧数据
    • 新数据加载完成后原子性替换

通过配置中心协调和同步

通过微服务中的配置中心(例如 Nacos)来协调,因为所有服务器都会连接到配置中心,所以当数据修改之后,可以修改配置中心的配置,然后配置中心再把配置变更的事件推送给各个服务,各个服务感知到配置中心的配置发生更改之后,再更新自己的本地缓存,这样就实现了本地缓存的数据一致性。

具体的流程描述

  1. Redis缓存发生变更 ‌(比如:某个业务修改了Redis里的数据)

    → ‌但Nacos本身不会监控Redis的变化 ‌,需要‌业务代码主动通知Nacos‌。

  2. 业务代码感知到Redis变更后,主动更新Nacos配置

    → 比如调用Nacos的API,更新一个特定的配置项(例如:cache_version=2)。

  3. Nacos将配置变更推送给所有监听的服务

    → 各个微服务通过‌长轮询(Long Polling) ‌或‌WebSocket‌实时接收Nacos的配置变更通知。

  4. 各服务收到Nacos的通知后,主动更新本地缓存

    → 比如:

    • 直接清空本地缓存(下次访问时重新加载)
    • 或者调用Redis获取最新数据,更新本地缓存

高频问题

1. Nacos配置监听机制
  • 核心问题 ‌:如何通过Nacos监听配置变更?
    • 回答要点 ‌:
      • 客户端通过addListener订阅配置(Data ID + Group)
      • 采用‌**长轮询(Long Polling)**‌机制,默认30秒超时,配置变更后立即推送
      • 监听器Listener接口实现receiveConfigInfo方法处理变更逻辑

2. 数据一致性保障
  • 核心问题 ‌:如何保证Redis与本地缓存的数据一致性?
    • 回答要点 ‌:
      • 版本号设计‌:通过Nacos配置中的版本号(如时间戳)标记变更
      • 最终一致性‌:依赖Nacos的AP模式(Distro协议)实现异步同步
      • 本地缓存更新策略‌:清空缓存或主动拉取最新数据

‌3. 多级缓存协同‌
  • 核心问题 ‌:Redis、Nacos、本地缓存如何协同工作?
    • 回答要点 ‌:
      1. Redis变更 → 业务代码主动更新Nacos版本号
      2. Nacos推送变更 → 服务更新本地缓存(如Caffeine)
      3. 降级策略‌:本地缓存未命中时回源Redis

4. 性能与可靠性
  • 核心问题 ‌:如何避免频繁更新导致的性能问题?
    • 回答要点 ‌:
      • 批量更新‌:合并短时间内的多次变更,减少推送频率
      • 本地缓存过期时间‌:设置合理的TTL(如30分钟)
      • 容错机制‌:Nacos集群故障时降级为读取本地缓存

‌5**. 对比其他方案**‌
  • 核心问题 ‌:与直接监听Redis的Pub/Sub有何区别?
    • 回答要点 ‌:
      • Nacos优势‌:解耦业务逻辑,支持配置版本管理
      • Pub/Sub劣势‌:需维护Redis连接,无历史版本回溯能力

分布式缓存与数据库如何保证一致性

四大同步策略

策略顺序 优点 缺点 适用场景
先更新缓存再更新数据库 缓存命中率高 第二步失败导致缓存脏数据 不推荐使用
先更新数据库再更新缓存 相对安全 第二步失败导致缓存旧数据 低并发场景
先删除缓存再更新数据库 操作简单 缓存击穿风险,数据不一致 不推荐使用
‌**先更新数据库再删除缓存(推荐)**‌ 影响最小 删除失败导致旧数据 大多数场景

延时双删

针对上面这种情况,我们有一种延时双删的方法

1)删除缓存

2)更新数据库

3)休眠 500ms(这个时间,依据读取数据的耗时而定)

4)再次删除缓存

这时候唯一存在的一个问题就是,在(更新据库 + 休眠 500 ms) 这个时间窗口中,依旧能读取到旧值,而这个短暂时间控制的好的话,是可以接受的。

**基于消息队列的异步同步(如Canal+MQ)**‌

  1. 工作原理
    • 通过监听数据库的binlog(如MySQL的ROW模式),使用Canal捕获增删改事件并推送至消息队列(Kafka/RocketMQ)‌。
    • 消费者服务订阅消息,根据事件类型删除或更新缓存,实现最终一致性‌
相关推荐
猿月亮14 分钟前
Java设计模式之-组合模式
java·设计模式·组合模式
uppp»16 分钟前
Springboot配置文件
java·spring boot·后端
CYRUS_STUDIO29 分钟前
一文搞懂 Smali 与 Baksmali:Java 层逆向必备技能
android·java·逆向
慕y27432 分钟前
Java学习第六十部分——JVM
java·开发语言·学习
Jinkxs32 分钟前
Java 性能调优实战:JVM 参数配置与 GC 日志分析
java·jvm·测试工具
白仑色1 小时前
JavaScript 语言基础详解
开发语言·javascript·ecmascript·前端开发
超级小忍1 小时前
从 Spring Boot 2.x 到 Spring Boot 3.x:全面对比与快速上手指南
java·spring boot·后端
Andy杨1 小时前
20250720-4-Kubernetes 调度-指定节点调度:nodeSelector&nodeAffinity笔记
java·docker·kubernetes
AI、少年郎2 小时前
从 C# 转 Python 第三天:文件操作、异常处理与错误日志实践
java·前端·数据库·c#·异常处理·文件操作
节省钱2 小时前
【Flutter】深入理解 Provider:不仅仅是Consumer
开发语言·前端·flutter·前端框架