【PmHub面试篇】PmHub 缓存与数据库一致性的面试专题分析

发布于:2025-06-18 ⋅ 阅读:(18) ⋅ 点赞:(0)

在分布式系统开发中,缓存与数据库的一致性问题是后端开发面试的核心考点之一。本文结合 PmHub 项目实践,整理高频面试题及深度解答,帮助开发者系统掌握缓存一致性解决方案的设计与实现。若想对相关内容有更透彻的理解,强烈推荐参考之前发布的博文:【PmHub后端篇】PmHub 中缓存与数据库一致性的实现方案及分析

1 项目中如何保证缓存和数据一致性

我们主要采用Cache Aside 模式,核心逻辑是:

  • 读流程
    • 先查询缓存,若命中直接返回
    • 若未命中,查询数据库
    • 将查询结果写入缓存(设置合理过期时间)
  • 写流程:
    • 先更新数据库
    • 删除对应缓存(而非更新,原因见问题 2)

同时结合以下辅助策略:

  • 分布式锁:处理高并发写冲突(如流程状态更新场景,通过 Redisson 实现分布式锁)
  • 缓存监控:通过 SkyWalking 监控缓存命中率(目标 > 90%)、淘汰率(<5%)
  • 定时任务:每日凌晨对冷数据进行全量缓存重建

2 为什么删除缓存,而不更新缓存

主要基于以下考量:

  • 更新缓存浪费服务器资源:频繁的缓存更新可能导致缓存服务器的负载增加,通过删除缓存而不是频繁更新,可以减少缓存服务器的压力,提高系统整体性能。
  • 避免脏数据:高并发下更新缓存可能读到中间态数据(如事务未提交)
  • 减少无效更新:频繁写场景下,删除缓存可避免缓存与 DB 的无效同步

3 先更新 DB,再删除缓存是否是完美解决方案

  • 局限性分析
    1. 写性能损耗:每次写操作需额外执行缓存删除(优化方向:通过异步队列批量处理缓存失效)

    2. 极端并发不一致
      场景:线程 A 更新 DB 后未删缓存时,线程 B 读取到旧缓存
      概率:仅发生在 DB 更新成功但缓存删除失败,且此时有读请求的极端情况
      解决方案

      • 缓存设置短过期时间(如 5 分钟)
      • 增加异步校验任务(定时对比 DB 与缓存数据)
    3. 缓存穿透与预热问题

    • 冷数据首次访问会穿透到 DB(布隆过滤器拦截无效请求)
    • 系统冷启动时需预热热点数据(启动时通过 Spring 事件监听异步加载)

4 Cache Aside 模式有哪些局限性?

  • 缓存穿透:首次访问冷数据时存在 DB 穿透风险(解决方案:布隆过滤器拦截无效 key)
  • 并发不一致:极端情况下(如更新 DB 后删除缓存前服务宕机)可能不一致(解决方案:分布式锁 + 异步数据校验任务)
  • 缓存预热:冷启动时需要预热热点数据(解决方案:启动时通过 @PostConstruct 预热或异步线程加载)
  • 写性能影响:每次写操作需额外执行缓存删除(优化方向:合并批量写操作,使用 Redis Pipeline 批量删除)

5 Spring Cache 如何集成 Redis?

Spring Cache 默认使用本地缓存(ConcurrentMap),集成 Redis 需:

  1. 添加 Redis 缓存依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 配置 CacheManager:
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(RedisConnectionFactory connectionFactory) {
    return builder -> builder
        .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig(connectionFactory))
        .withCacheConfiguration("yourCacheName", 
            RedisCacheConfiguration.defaultCacheConfig(connectionFactory)
                .entryTtl(Duration.ofMinutes(5))
        );
}
  1. 使用 @Cacheable/@CacheEvict 注解声明缓存操作。

6 如果缓存删除失败怎么办?

采用重试机制 + 消息队列

  • 删除缓存失败时,将 key 写入 RabbitMQ 死信队列
  • 通过消费者异步重试删除(设置 3 次重试间隔,避免洪峰)

7 如何处理缓存雪崩?

  • 缓存层加随机过期时间(如 10-15 分钟随机)
  • 对热点数据加本地缓存(Caffeine)作为保护罩
  • 服务层添加 Hystrix 熔断,防止 DB 被压垮

6 参考链接

PmHub如何保证缓存和数据库的一致性


网站公告

今日签到

点亮在社区的每一天
去签到