缓存和数据库,1+1如何大于2?

一、缓存的本质

缓存,简单说就是为了节约对原始资源重复获取的开销,而将结果数据副本存放起来以供获取的方式。

首先,缓存往往针对的是"资源"。我们前面已经多次提到过,当某一个操作是"幂等"的和"安全"的,那么这样的操作就可以被抽象为对"资源"的获取操作,那么它才可以考虑被缓存。有些操作不幂等、不安全,比如银行转账,改变了目标对象的状态,自然就难以被缓存。

其次,缓存数据必须是"重复"获取的。缓存能生效的本质是空间换时间。也就是说,将曾经出现过的数据以占据缓存空间的方式存放下来,在下一次的访问时直接返回,从而节约了通过原始流程访问数据的时间。有时候,某些资源的获取行为本身是幂等的和安全的,但实际应用上却不会"重复"获取,那么这样的资源是无法被设计成真正的缓存的。我们把一批数据获取中,通过缓存获得数据的次数,除以总的次数,得到的结果,叫做缓存的命中率。

再次,缓存是为了解决"开销"的问题。这个开销,可不只有时间的开销。虽然我们在很多情况下讲的开销,确实都是在时间维度上的,但它还可以是CPU、网络、I/O等一切资源。例如我们有时在Web服务中增加一层缓存,是为了避免了对原始资源获取的时候,对数据库资源调用的开销。

二、缓存应用模式

2.1 旁路缓存模式

2.1.1 数据读写策略

读数据时:

  • 先读缓存,若缓存有数据,直接返回
  • 若缓存没有,读数据库。若数据库有,将结果写入缓存,并返回结果
  • 若数据库没有,就返回没有

写数据时:

  • 先写数据库
  • 再令对应的缓存失效

2.1.2 操作关键

  • 写数据时,必须先更新数据库,再令缓存失效

这个很容易理解,如果先令缓存失效了,而数据库还没来得及更新成功,那么假如这个时候有一个请求访问,他会直接击穿到数据库中,带着数据库的陈旧值去更新缓存,就会导致旧数据长期存在于缓存中,导致严重的数据不一致问题。

  • 写数据时,更新完数据库之后,必须是让缓存失效,而不是更新缓存

为什么呢?如果此时更新的策略是更新缓存而不是令缓存失效,此时几乎同时发出的请求分别更新数据库中的值为A和B,结果是A的更新早于B,那么并不能保证这两个请求更新缓存时,顺序就是A早于B,就会导致缓存中的数据可能会长期是A值。

2.1.3 数据异常情形

读操作:

  • 缓存读取异常,直接返回失败,没有数据不一致的情况
  • 数据库读取异常,直接返回失败,没有数据不一致的情况
  • 数据库读取成功,但是缓存写入失败,那么下一次读取同一数据的请求还会继续尝试写入,没有数据不一致的情况发生

写操作:

  • 数据库写入失败,直接返回失败,没有数据不一致的情况
  • 数据库写入成功,但是缓存失效的操作失败,这个问题发生了之后会非常麻烦,需要特殊处理来纠正(比如缓存数据和数据库不一致时配置告警、定期将数据库数据刷缓存)

2.2 缓存代理模式

将缓存系统作为数据库的代理,应用的请求访问只能到缓存,数据库系统对应用来说是透明的。

2.2.1 数据读写策略

读操作:

  • 先读缓存,如果缓存中有数据,返回
  • 如果缓存中没有数据,缓存查询数据库,并将结果写入自己,再返回给应用

写操作:

  • 先写缓存
  • 缓存再更新数据库
  • 通知应用写入成功
    • 这里更新数据库有两种策略
    • 透写(write-through):同步更新数据库完成之后再返回成功
    • 回写(write-back):更新缓存之后就直接返回成功,异步更新数据库(支持批量更新,更新效率高,速率稳定;但是存在数据丢失风险)

2.2.2 操作关键点

  • 缓存系统需要自己内部保证并发场景下,缓存更新的顺序和数据库更新的顺序一致,这个可以用乐观锁来保证

2.2.3 数据异常情形

读操作:

  • 同旁路缓存模式,没有数据不一致情况

写操作:

  • 如果缓存更新失败,直接返回失败,没有数据不一致情况
  • 如果缓存更新成功,数据库更新失败,需要回滚
相关推荐
喵叔哟1 天前
【.NET 8 实战--孢子记账--从单体到微服务】--简易权限--访问权限中间件
微服务·中间件·.net
青锐CC1 天前
webman使用中间件验证指定的控制器及方法[青锐CC]
中间件·前端框架·php
前端 贾公子1 天前
Koa进阶:掌握中间件和参数校验的艺术
中间件
你的微笑,乱了夏天2 天前
微服务链路追踪skywalking安装
分布式·后端·中间件·架构·skywalking
激流丶2 天前
【缓存策略】你知道 Write Through(直写)这个缓存策略吗?
java·分布式·后端·缓存·中间件
CodingBrother2 天前
RabbitMQ高效的消息队列中间件原理及实践
中间件·rabbitmq
LightOfNight2 天前
Redis设计与实现第9章 -- 数据库 总结(键空间 过期策略 过期键的影响)
数据库·redis·后端·缓存·中间件·架构
许苑向上3 天前
在Linux上部署(MySQL Redis Elasticsearch等)各类软件
linux·中间件·开源软件
网安_秋刀鱼3 天前
中间件安全
安全·web安全·网络安全·中间件·1024程序员节
凡人的AI工具箱4 天前
15分钟学 Go 第 41 天:中间件的使用
开发语言·人工智能·后端·中间件·golang