Redis 中如何保证缓存与数据库的数据一致性?

发布于:2025-05-31 ⋅ 阅读:(21) ⋅ 点赞:(0)

在 Redis 中保证缓存与数据库的数据一致性,需结合业务场景选择以下策略:

核心策略总结

  1. Cache Aside(旁路缓存)模式

    • 读操作:先查缓存,未命中则查数据库并写入缓存。
    • 写操作:先更新数据库,再删除缓存(推荐延迟双删或异步重试)。
    • 适用场景:大多数业务,实现简单但需处理并发不一致。
  2. Write Through(写穿透)模式

    • 应用程序仅操作缓存,由缓存同步更新数据库。
    • 适用场景:对一致性要求高但写入频率低的简单数据模型。
  3. Write Behind(异步缓存写入)模式

    • 写操作仅更新缓存,异步批量同步到数据库。
    • 适用场景:非关键数据(如日志、浏览量),追求极致性能。
  4. 数据库变更通知(Binlog监听)

    • 通过 Canal 等工具订阅数据库 Binlog,触发缓存更新。
    • 适用场景:需要实时性强的强一致性场景。
  5. 分布式锁与原子操作

    • 通过锁机制保证数据库和缓存操作的原子性。
    • 适用场景:强一致性要求极高的核心业务(如金融交易)。
  6. 缓存失效策略

    • 设置合理的 TTL,平衡缓存命中率和一致性需求。
    • 适用场景:允许短暂不一致的通用数据(如商品信息)。

方案选择建议

  • 强一致性场景(如金融、订单):
    优先选择 Cache Aside + 延迟双删数据库变更通知,结合重试机制。
  • 最终一致性场景(如商品库存、用户信息):
    使用 Cache Aside + 合理 TTL,降低实现复杂度。
  • 高并发写场景(如日志、点赞数):
    使用 Write Behind 模式,牺牲部分一致性换取性能。

关键实现细节

  • 延迟双删:更新数据库后,延迟一段时间(如 100ms)再次删除缓存,覆盖并发读请求。
  • 重试机制:删除缓存失败时,将任务写入消息队列异步重试。
  • 监控与告警:建立缓存命中率、数据库负载等指标的监控,及时发现不一致问题。

代码示例(Cache Aside + 延迟双删)

// 写操作
public void updateUser(User user) {
    userRepository.save(user);        // 更新数据库
    redisService.delete("user:" + user.getId()); // 第一次删除缓存
    CompletableFuture.runAsync(() -> { // 异步延迟双删
        try { Thread.sleep(100); } catch (InterruptedException e) {}
        redisService.delete("user:" + user.getId()); // 第二次删除
    });
}

// 读操作
public User getUser(Long id) {
    String key = "user:" + id;
    User user = redisService.get(key);
    if (user == null) {
        user = userRepository.findById(id).orElse(null);
        if (user != null) { redisService.set(key, user); } // 写入缓存
    }
    return user;
}

总结

  • 无银弹:一致性、性能和复杂度需权衡,根据业务场景选择策略。
  • 最终一致性:在分布式系统中,完全的强一致性难以保证,需通过异步任务、消息队列等方式确保数据最终一致。

我正在程序员刷题神器面试鸭上高效准备面试,9000+ 高频面试真题、800 万字优质题解,覆盖主流编程方向,跟我一起刷原题、过面试:
点击进入