一文读懂 Redis 缓存系统( 三 )


在大多数场景下,我们通常使用通读和直写/后写/写无效等模型 。针对 Refresh-ahead 模型 , 其可以单独使用,也可以作为一种优化来预测和预热读取以进行通读 。由谁负责缓存维护,调用者或专用层有两种实现模式 。
1、Cache-Facade:缓存层是一个库或服务委托写入数据库,我们只与缓存层交谈 。然后数据库对我们的应用程序是透明的 。缓存层可以处理一致性和故障转移 。例如,许多数据库都有自己的缓存,这是缓存外观的一个很好的例子 。我们还可以编写一些进程内 DAO 层来读取/写入具有嵌入式缓存层的实体,从调用者的角度来看 , 这个小层也是一个缓存门面 。
2、Cache-Aside:我们的应用程序保持缓存一致性,这意味着应用程序代码更复杂 , 但这提供了更大的灵活性 。例如,像数据库查询缓存这样的缓存外观模式只能缓存行,如果想缓存带有行的 JAVA POJO 或 Kotlin 数据类,则将缓存放在一边要容易得多 。但是它仍然可以使用缓存门面,例如,将 Spring 缓存作为门面库来缓存 POJO,并在后台自动处理数据库中的 POJO 。
当缓存不支持原生的读通和写通操作 , 并且资源需求不可预测时,我们使用这种缓存侧模式 。
1)读?。撼⑹悦?谢捍?。如果没有命中 , 则从数据库中读?。?缓蟾?禄捍?。
2)写入:先写入数据库,然后删除缓存条目 。这里一个常见的陷阱是人们错误地用值更新了缓存,高并发环境下的双写会使缓存变脏 。
在这种模式下,仍然有可能出现脏缓存 。在满足这两种情况时会发生上述情况:读取数据库并更新缓存、 更新数据库并删除缓存 。
缓存一致性

一文读懂 Redis 缓存系统

文章插图
缓存一致性模型(参考)图
如何保障缓存(Redis)与 数据存储(数据库)之间的数据一致性 , 通常 有多种 设计实现策略,本文重点针对 Cache Aside Pattern(旁路缓存模式) 进行简要解析,此模型也是在实际的业务场景中使用较为广泛的 。具体如下 。
在 Cache Aside Pattern 模型中,通常写请求场景基本流程主要为:先更新 DB,然后直接删除 Cache。
在业务场景实现中,如果更新数据库成功,而进行缓存删除操作时出现失败的情况下,简单地说 , 通常主要有以下两个解决方案:
1、缩短 Cache 失效时间:我们让缓存数据的过期时间变短,这样的话缓存就会从数据库中加载数据 。另外,这种解决办法对于先操作缓存后操作数据库的场景不适用 。此方案在实际的业务场景中通常 不推荐,本质上治标不治本 。
2、增加 Cache 更新重试机制:如果 Cache 服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试 , 重试次数可以自己定 。如果多次重试还是失败的话,我们可以把当前更新失败的 Key 存入队列中 , 等缓存服务可用之后 , 再将缓存中对应的 Key 删除即可 。可考虑使用消息队列 。此方案算是一种 常用的解决策略,能够满足绝大多数业务场景需要 。
其实,从本质上而言,缓存方案的规划设计往往依赖于实际的业务场景需求,毕竟,技术是为业务服务的 。可能有时我们引入缓存之后,为了解决短期内的不一致性问题,选择让系统设计变得更加复杂的话,完全没必要 。
缓存异常场景
一文读懂 Redis 缓存系统

文章插图
缓存场景模型图
其实 。在实际的场景中,考虑到各种应用异常和业务故障,通常不可能完全使用分布式缓存和数据库系统来实现线性一致性模型 。每一种缓存模式都有其自身的局限性,在某些情况下我们无法获得顺序一致性,或者有时会在缓存和数据库之间获得意外延迟 。对于笔者在本文中展示的所有的解决方案,依据不同的业务需求总是会遇到高并发的极端情况 。因此 , 对此没有灵丹妙药,在选择解决方案之前了解限制并定义特定的一致性要求 。
如果想要实现线性一致性和容错性 , 建议最好不要使用缓存策略,可考虑其他的方案 。以上为 Redis 缓存系统相关解析 , 希望对大家有用 。
作者:李杰
来源:twt社区




推荐阅读