缓存3大痛点:
- Redis 缓存满了怎么办?
- 缓存穿透、缓存击穿、缓存雪崩如何解决?
- Redis 数据过期了会被立马删除么?
- Redis 突然变慢了如何做性能排查并解决?
- Redis 与 MySQL 数据一致性问题怎么应对?
缓存共识
- 缓存必有过期时间;
- 数据库、缓存的最终一致性,不必强一致性。
- 保持强一致性,势必要引入 2PC 或 Paxos 等分布式一致性协议,或者分布式锁
缓存使用策略
- Cache-Aside Pattern(旁路缓存,业务系统常用)
- Read-Through Pattern
- Write-Through Pattern
- Write-Behind Pattern
一致性解决方案有哪些?
- 先删除缓存,再更新数据库
- 删除缓存重试机制
高并发的场景下,重试最好使用异步方式,比如发送消息到 mq 中间件,实现异步解耦。重试机制第(5)步如果删除失败且未达到重试最大次数则将消息重新入队,直到删除成功,否则就记录到数据库,人工介入。该方案有个缺点,就是对业务代码中造成侵入,于是就有了下一个方案,启动一个专门订阅 数据库 binlog 的服务读取需要删除的数据进行缓存删除操作。
- 读取 binlog 异步删除
binlog异步删除
更新数据库;
数据库会把操作信息记录在 binlog 日志中;
使用 canal 订阅 binlog 日志获取目标数据和 key;
缓存删除系统获取 canal 的数据,解析目标 key,尝试删除缓存。
如果删除失败则将消息发送到消息队列;
缓存删除系统重新从消息队列获取数据,再次执行删除操作。
总结
缓存策略的最佳实践是 Cache Aside Pattern。分别分为读缓存最佳实践和写缓存最佳实践。
读缓存最佳实践:先读缓存,命中则返回;未命中则查询数据库,再写到缓存中。
写缓存最佳实践:先写数据库,再操作缓存;
直接删除缓存,而不是修改。缓存的更新成本很高,需要访问多张表联合计算。
在以上最佳实践下,为了尽可能保证缓存与数据库的一致性,我们可以采用延迟双删。防止删除失败,我们采用异步重试机制保证能正确删除,异步机制我们可以发送删除消息到 mq 消息中间件,或者利用 canal 订阅 MySQL binlog 日志监听写请求删除对应缓存。
那么,如果我非要保证绝对一致性怎么办?
结论:没有办法做到绝对的一致性,这是由 CAP 理论决定的,缓存系统适用的场景就是非强一致性的场景,所以它属于 CAP 中的 AP。所以,我们得委曲求全,可以去做到 BASE 理论中说的最终一致性。其实一旦在方案中使用了缓存,那往往也就意味着我们放弃了数据的强一致性,但这也意味着我们的系统在性能上能够得到一些提升。