缓存一致性
缓存一致性是缓存架构中最复杂的问题之一。当数据更新时,如何保证缓存和数据库的数据一致,是一个需要权衡的问题。强一致性的方案通常性能较差,最终一致性的方案实现复杂。
一致性要求
若无特别要求,互联网应用一般采用最终一致性策略。
强一致性
强一致性要求缓存和数据库的数据始终一致,任何时刻读取到的数据都是最新的。实现强一致性需要使用分布式事务(如 2PC、3PC)或同步更新,性能开销大。
强一致性适合对数据准确性要求高的场景,如金融交易、库存扣减。但强一致性的实现复杂,性能差,大多数场景不适用。
最终一致性
最终一致性允许缓存和数据库的数据在短暂时间内不一致,但保证最终会一致。最终一致性是大多数缓存场景的选择,平衡了性能和一致性。
最终一致性的实现方式:异步更新、延迟双删、监听 Binlog、消息队列。
弱一致性
弱一致性不保证缓存和数据库的数据最终一致,可能出现长时间不一致。弱一致性适合对数据准确性要求不高的场景,如访问统计、热门排行。
更新策略
1. 更新数据库后更新缓存
更新数据库后,再更新缓存。如果缓存更新失败,数据库和缓存不一致。
更新缓存的优势:读操作命中率高(缓存始终有数据)。更新缓存的问题:并发写时可能导致脏数据、更新缓存失败时数据不一致。
2. 更新数据库后删除缓存
更新数据库后,删除缓存。下次读取时,缓存未命中,从数据库加载并写入缓存。
删除缓存的优势:避免并发写的脏数据、实现简单。删除缓存的问题:删除缓存前有读请求,可能导致脏数据。
3. 先删除缓存后更新数据库
先删除缓存,再更新数据库。如果删除缓存和更新数据库之间有读请求,读请求从数据库加载旧数据到缓存,导致缓存和数据库不一致。
这种方案的问题更大,不推荐使用。
4. 延迟双删
先删除缓存,更新数据库,延迟后再删除缓存。延迟双删可以减少不一致窗口期。
延迟双删的流程:
- 删除缓存
- 更新数据库
- 延迟一段时间(如 500ms)
- 再次删除缓存
延迟双删的优势:减少不一致窗口期。延迟双删的问题:延迟时间难以确定、实现复杂、可能仍有不一致。
5. 分布式锁
使用分布式锁(如 Redis SETNX、ZooKeeper)保证同一时刻只有一个请求可以更新缓存和数据库。
分布式锁的流程:
- 获取分布式锁
- 删除缓存
- 更新数据库
- 释放分布式锁
分布式锁的优势:强一致性。分布式锁的问题:性能差(锁竞争)、实现复杂、锁超时问题。
6. 消息队列
使用消息队列(如 Kafka、RabbitMQ)异步更新缓存。更新数据库后,发送消息到消息队列,消费者消费消息后更新缓存。
消息队列的流程:
- 更新数据库
- 发送消息到消息队列
- 消费者消费消息
- 更新缓存
消息队列的优势:解耦、异步、提高性能。消息队列的问题:实现复杂、消息丢失、顺序问题。
7. 监听 Binlog
通过监听数据库的 Binlog,异步更新缓存。数据库更新后,Binlog 记录更新操作,Canal、Debezium 等工具解析 Binlog,发送消息到消息队列,消费者消费消息后更新缓存。
监听 Binlog 的流程:
- 更新数据库
- 数据库写入 Binlog
- Canal 解析 Binlog
- 发送消息到消息队列
- 消费者消费消息
- 更新缓存
监听 Binlog 的优势:保证最终一致性、解耦数据库和缓存。监听 Binlog 的问题:实现复杂、需要 Canal、Debezium 等工具、延迟较高。
缓存一致性方案对比
| 方案 | 一致性 | 性能 | 实现复杂度 | 适用场景 |
|---|---|---|---|---|
| 更新数据库后更新缓存 | 弱 | 高 | 简单 | 读多写少、容忍短暂不一致 |
| 更新数据库后删除缓存 | 最终 | 高 | 简单 | 大多数场景 |
| 延迟双删 | 最终 | 中 | 中等 | 写操作频繁、一致性要求高 |
| 分布式锁 | 强 | 低 | 复杂 | 金融交易、库存扣减 |
| 消息队列 | 最终 | 中 | 复杂 | 大规模系统、解耦要求 |
| 监听 Binlog | 最终 | 中 | 复杂 | 大规模系统、保证最终一致性 |
缓存一致性的最佳实践
- 根据场景选择一致性级别:读多写少的场景使用删除缓存,写操作频繁的场景使用延迟双删,金融场景使用分布式锁。
- 设置合理的过期时间:即使缓存和数据库不一致,缓存过期后会自动从数据库加载最新数据。
- 监控缓存一致性:监控缓存的命中率、数据库的查询量,异常时及时告警。
- 增量更新:对于频繁更新的数据,使用增量更新而非全量更新,减少不一致的范围。
- 版本号:使用版本号或时间戳判断缓存的新旧,避免使用旧数据覆盖新数据。
- 缓存预热:系统启动时预加载热点数据到缓存,减少启动时的不一致。
- 缓存一致性是缓存架构的难点,理解各种方案的原理和权衡,有助于设计合适的缓存一致性方案。没有银弹,根据场景选择合适的方案才是最重要的。