Administrator
Published on 2025-03-09 / 5 Visits
0
0

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

在 Redis 中保证缓存与数据库的数据一致性是一个复杂的挑战,因为两者属于不同的系统(缓存是内存数据库,数据库是持久化存储),无法通过分布式事务直接实现强一致性。以下是常见的解决方案和最佳实践,根据业务场景选择合适策略:


缓存和数据库的同步可以通过以下几种方式:

1)先更新缓存,再更新数据库

2)先更新数据库存,再更新缓存

3)先删除缓存,再更新数据库,后续等查询把数据库的数据回种到缓存中

4)先更新数据库,再删除缓存,后续等查询把数据库的数据回种到缓存中

5)缓存双删策略。更新数据库之前,删除一次缓存;更新完数据库后,再进行一次延迟删除

6)使用 Binlog 异步更新缓存,监听数据库的 Binlog 变化,通过异步方式更新 Redis 缓存

以上就是实现数据库与缓存一致性的六种方式,这里前面三种都不太推荐使用,后面三种的话其主要根据实际场景:

如果是要考虑实时一致性的话,先写 MySQL,再删除 Redis 应该是较为优的方案,虽然短期内数据可能不一致,不过其能尽量保证数据的一致性。
如果考虑最终一致性的话,推荐的是使用 binlog + 消息队列的方式,这个方案其有重试和顺序消费,能够最大限度地保证缓存与数据库的最终一致性。


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

最常见的模式,核心思想是应用层主动管理缓存

  • 读流程
    1. 先读缓存,命中则返回数据。
    2. 缓存未命中时,从数据库读取数据。
    3. 将数据写入缓存(设置合理的过期时间)。
  • 写流程
    1. 先更新数据库
    2. 删除缓存(而非更新缓存,避免并发写导致脏数据)。

关键问题与优化

  • 并发读写导致脏数据
    • 场景:A 写数据库 → 删除缓存 → B 读缓存未命中 → B 读旧数据并回填缓存。
    • 优化:使用延迟双删(写后 sleep 短暂时间再删一次缓存)或设置较短的缓存过期时间。
  • 删除缓存失败
    • 引入重试机制(如通过消息队列异步重试),确保最终删除成功。

2. Write-Through/Read-Through(穿透读写)模式

缓存层代理数据库的读写操作:

  • 写流程
    1. 先更新缓存。
    2. 缓存层同步更新数据库。
  • 读流程
    1. 请求直接访问缓存,若未命中则由缓存层从数据库加载。

适用场景

  • 对一致性要求较高,且缓存支持此类逻辑(如某些缓存库或中间件)。

3. Write-Behind(异步回写)模式

  • 写流程
    1. 先更新缓存。
    2. 异步批量更新数据库(如合并多次写操作)。
  • 优点:高性能,减少数据库压力。
  • 缺点:存在数据丢失风险(缓存宕机时),仅适用于允许数据最终一致的场景。

4. 基于消息队列的最终一致性

  • 流程
    1. 更新数据库后,发送一条消息到 MQ(如 Kafka/RabbitMQ)。
    2. 消费者监听 MQ,异步删除或更新缓存。
  • 优点:解耦数据库与缓存操作,支持重试。
  • 注意:需处理消息重复消费(幂等设计)。

5. 订阅数据库变更日志(如 Binlog)

  • 流程
    1. 使用工具(如 Canal、Debezium)订阅数据库的 Binlog。
    2. 解析日志后,触发缓存删除/更新操作。
  • 优点:无侵入性,保证缓存与数据库严格同步。
  • 适用场景:对一致性要求高,且架构支持监听数据库日志。

6. 强一致性方案(慎用)

通过分布式锁或事务保证原子性,但性能代价高:

  • 流程
    1. 加锁(如 Redis 分布式锁)。
    2. 更新数据库 → 删除缓存 → 释放锁。
  • 缺点:锁竞争可能成为瓶颈,仅适用于对一致性要求极高的场景。

策略选择建议

场景推荐方案一致性级别
读多写少,允许短暂不一致Cache-Aside + 缓存过期时间最终一致性
写频繁,允许异步Write-Behind + 消息队列最终一致性
高一致性要求订阅 Binlog 或强一致性方案强/最终一致性
简单业务场景延迟双删 + 重试机制最终一致性

关键注意事项

  1. 优先删除缓存,而非更新缓存:避免并发写导致缓存与数据库不一致。
  2. 设置缓存过期时间:作为兜底策略,防止永久不一致。
  3. 降级策略:缓存故障时,应有直接读数据库的能力。
  4. 监控与告警:跟踪缓存命中率、延迟、不一致情况。

通过合理组合上述策略,可以在性能与一致性之间找到平衡。通常推荐以 Cache-Aside 为主,结合消息队列或 Binlog 监听实现最终一致性。


Comment