Skip to content

缓存一致性

缓存一致性是缓存架构中最复杂的问题之一。当数据更新时,如何保证缓存和数据库的数据一致,是一个需要权衡的问题。强一致性的方案通常性能较差,最终一致性的方案实现复杂。

一致性要求

若无特别要求,互联网应用一般采用最终一致性策略。

强一致性

强一致性要求缓存和数据库的数据始终一致,任何时刻读取到的数据都是最新的。实现强一致性需要使用分布式事务(如 2PC、3PC)或同步更新,性能开销大。

强一致性适合对数据准确性要求高的场景,如金融交易、库存扣减。但强一致性的实现复杂,性能差,大多数场景不适用。

最终一致性

最终一致性允许缓存和数据库的数据在短暂时间内不一致,但保证最终会一致。最终一致性是大多数缓存场景的选择,平衡了性能和一致性。

最终一致性的实现方式:异步更新、延迟双删、监听 Binlog、消息队列。

弱一致性

弱一致性不保证缓存和数据库的数据最终一致,可能出现长时间不一致。弱一致性适合对数据准确性要求不高的场景,如访问统计、热门排行。

更新策略

1. 更新数据库后更新缓存

更新数据库后,再更新缓存。如果缓存更新失败,数据库和缓存不一致。

更新缓存的优势:读操作命中率高(缓存始终有数据)。更新缓存的问题:并发写时可能导致脏数据、更新缓存失败时数据不一致。

2. 更新数据库后删除缓存

更新数据库后,删除缓存。下次读取时,缓存未命中,从数据库加载并写入缓存。

删除缓存的优势:避免并发写的脏数据、实现简单。删除缓存的问题:删除缓存前有读请求,可能导致脏数据。

3. 先删除缓存后更新数据库

先删除缓存,再更新数据库。如果删除缓存和更新数据库之间有读请求,读请求从数据库加载旧数据到缓存,导致缓存和数据库不一致。

这种方案的问题更大,不推荐使用。

4. 延迟双删

先删除缓存,更新数据库,延迟后再删除缓存。延迟双删可以减少不一致窗口期。

延迟双删的流程:

  1. 删除缓存
  2. 更新数据库
  3. 延迟一段时间(如 500ms)
  4. 再次删除缓存

延迟双删的优势:减少不一致窗口期。延迟双删的问题:延迟时间难以确定、实现复杂、可能仍有不一致。

5. 分布式锁

使用分布式锁(如 Redis SETNX、ZooKeeper)保证同一时刻只有一个请求可以更新缓存和数据库。

分布式锁的流程:

  1. 获取分布式锁
  2. 删除缓存
  3. 更新数据库
  4. 释放分布式锁

分布式锁的优势:强一致性。分布式锁的问题:性能差(锁竞争)、实现复杂、锁超时问题。

6. 消息队列

使用消息队列(如 Kafka、RabbitMQ)异步更新缓存。更新数据库后,发送消息到消息队列,消费者消费消息后更新缓存。

消息队列的流程:

  1. 更新数据库
  2. 发送消息到消息队列
  3. 消费者消费消息
  4. 更新缓存

消息队列的优势:解耦、异步、提高性能。消息队列的问题:实现复杂、消息丢失、顺序问题。

7. 监听 Binlog

通过监听数据库的 Binlog,异步更新缓存。数据库更新后,Binlog 记录更新操作,Canal、Debezium 等工具解析 Binlog,发送消息到消息队列,消费者消费消息后更新缓存。

监听 Binlog 的流程:

  1. 更新数据库
  2. 数据库写入 Binlog
  3. Canal 解析 Binlog
  4. 发送消息到消息队列
  5. 消费者消费消息
  6. 更新缓存

监听 Binlog 的优势:保证最终一致性、解耦数据库和缓存。监听 Binlog 的问题:实现复杂、需要 Canal、Debezium 等工具、延迟较高。

缓存一致性方案对比

方案一致性性能实现复杂度适用场景
更新数据库后更新缓存简单读多写少、容忍短暂不一致
更新数据库后删除缓存最终简单大多数场景
延迟双删最终中等写操作频繁、一致性要求高
分布式锁复杂金融交易、库存扣减
消息队列最终复杂大规模系统、解耦要求
监听 Binlog最终复杂大规模系统、保证最终一致性

缓存一致性的最佳实践

  • 根据场景选择一致性级别:读多写少的场景使用删除缓存,写操作频繁的场景使用延迟双删,金融场景使用分布式锁。
  • 设置合理的过期时间:即使缓存和数据库不一致,缓存过期后会自动从数据库加载最新数据。
  • 监控缓存一致性:监控缓存的命中率、数据库的查询量,异常时及时告警。
  • 增量更新:对于频繁更新的数据,使用增量更新而非全量更新,减少不一致的范围。
  • 版本号:使用版本号或时间戳判断缓存的新旧,避免使用旧数据覆盖新数据。
  • 缓存预热:系统启动时预加载热点数据到缓存,减少启动时的不一致。
  • 缓存一致性是缓存架构的难点,理解各种方案的原理和权衡,有助于设计合适的缓存一致性方案。没有银弹,根据场景选择合适的方案才是最重要的。