Redis与数据库的双写一致性是分布式系统中常见的挑战,核心目标是在缓存(Redis)和数据库之间维持数据的一致性。以下是常见的解决方案及其实现逻辑:


1. 缓存更新策略

1.1 Cache-Aside(旁路缓存)

  • 核心逻辑:由应用层管理缓存和数据库的读写。

    • 读操作
      1. 先读缓存,命中则返回;
      2. 未命中则读数据库,回填缓存。
    • 写操作
      1. 先更新数据库;
      2. 再删除缓存(而非更新,避免并发写导致脏数据)。
  • 优点:简单、通用,适用于读多写少场景。

  • 缺点

    • 短暂不一致:若数据库更新后、缓存删除前有读请求,会读到旧数据。
    • 需处理缓存删除失败(需重试或异步补偿)。

1.2 Write-Through(直写)

  • 核心逻辑:所有写操作同时更新数据库和缓存。

    • 写操作
      1. 先更新缓存;
      2. 同步更新数据库。
  • 优点:强一致性。

  • 缺点

    • 写入性能低(需同时写缓存和数据库);
    • 不适用于写多场景。

2. 双删策略

延迟双删

  • 核心逻辑

    1. 先删除缓存;
    2. 更新数据库;
    3. 延迟一段时间后再次删除缓存(确保主从同步完成后的旧数据清理)。
  • 适用场景:数据库主从架构,存在同步延迟。

  • 关键参数:延迟时间需大于主从同步时间(如500ms)。

  • 缺点:依赖延迟时间的准确性。


3. 异步补偿

3.1 基于消息队列

  • 核心逻辑

    1. 更新数据库;
    2. 发送消息到MQ,异步删除缓存;
    3. 消费端重试失败任务。
  • 优点:解耦、保证最终一致性。

  • 缺点:系统复杂度增加,需维护MQ。


3.2 基于数据库Binlog(如Canal)

  • 核心逻辑

    1. 数据库更新后,通过Binlog(如MySQL)捕获变更;
    2. 通过中间件(如Canal)解析Binlog;
    3. 异步更新或删除缓存。
  • 优点:无侵入性,保证最终一致性。

  • 缺点:依赖数据库日志同步机制,延迟可能较高。


4. 强一致性方案

4.1 分布式锁

  • 核心逻辑

    1. 写操作前获取分布式锁;
    2. 更新数据库后删除缓存;
    3. 读操作若缓存未命中,需先获取锁再查数据库。
  • 优点:强一致性。

  • 缺点:性能低,复杂度高。


4.2 版本号/时间戳

  • 核心逻辑

    1. 数据写入时附带版本号;
    2. 更新时校验版本号,防止旧数据覆盖;
    3. 缓存中存储版本号,读请求需校验。
  • 适用场景:对一致性要求极高的场景(如库存扣减)。

  • 缺点:实现复杂,需维护版本号。


5. 方案对比与选择

方案 一致性级别 性能 复杂度 适用场景
Cache-Aside 最终一致 读多写少
延迟双删 最终一致 主从同步延迟场景
异步补偿(MQ/Binlog) 最终一致 高并发、最终一致场景
分布式锁 强一致 对一致性要求极高的场景

6. 最佳实践

  1. 优先最终一致性:大多数场景不需要强一致,可通过异步补偿或延迟双删实现。
  2. 监控与告警:监控缓存与数据库不一致的时长,设置阈值告警。
  3. 兜底策略
    • 缓存设置合理的TTL,避免长期不一致;
    • 关键数据(如库存)采用强一致性方案。

总结

选择方案需权衡业务场景(一致性要求、读写比例)、系统复杂度及性能。例如:

  • 电商商品详情页:Cache-Aside + 异步补偿;
  • 金融交易系统:分布式锁 + 版本号。