天天看点

缓存与数据库一致性前言一、缓存是什么?为什么引入缓存?二、缓存一致性总结

缓存与数据库一致性

  • 前言
  • 一、缓存是什么?为什么引入缓存?
    • 1.缓存的引入
    • 2.读写的性能提升
  • 二、缓存一致性
    • 1.谈谈一致性
    • 2.三种经典的缓存模式
    • 3.讨论三种数据更新方案
  • 总结

前言

面试的时候被问到了不少关于缓存的问题,而缓存的一致性问题是被问的最多的,当时没怎么答好,这次把关于缓存的问题都记录下来,以做积累和沉淀。

参考文档:https://mp.weixin.qq.com/s/urm2W1rBmIGa09w8K-hqvA

https://mp.weixin.qq.com/s/a4FRIKu1YWftnso2iBvWSw

一、缓存是什么?为什么引入缓存?

缓存的本质:弥补cpu的高算力和IO的慢读写之间巨大的鸿沟。

1.缓存的引入

  1. 在初期业务量小的时候,数据库能承担读写压力,应用可以直接和DB交互,架构简单且强壮。
  2. 当业务量大规模增加时,DB查询压力和耗时都在增长。此时引入分布式缓存,减少DB压力的同时,还提供了高qps。
  3. 发展到后面,分布式缓存也成为瓶颈,高频的qps成为负担,另外缓存驱逐(缓存淘汰)和网络抖动会影响系统的稳定性,此时引入本地缓存,可以减轻分布式缓存的压力,并减少网络和序列化开销。

2.读写的性能提升

  • 读优化:当请求命中缓存后,可直接返回,从而略过IO读取,减小读的成本。
  • 写优化:将写操作在缓冲中合并,让IO设备可以批量处理,减小写的成本。

二、缓存一致性

1.谈谈一致性

一致性分为三种类型:

  • 强一致性:这种一致性级别是最符合用户直觉的。它要求系统写入什么,读出来就是什么,用户体验好,但是实现起来对系统的性能影响大。【CAP理论】
  • 弱一致性:这种级别约束了系统在写入成功后,不保证立即可以读到写入的值,也不保证多久之后数据能够达到一致,但会尽可能保证某个时间级别后,数据能够达到一致状态。
  • 最终一致性:最终一致性是弱一致性的一个特例,系统会保证在一定时间内,能够达到数据一致的状态。业界大型系统较推崇的一种方式。

2.三种经典的缓存模式

有三种经典的缓存模式:

  • cache-aside
  • read/write through
  • write behind

cache-aside

  1. 读请求流程:
    缓存与数据库一致性前言一、缓存是什么?为什么引入缓存?二、缓存一致性总结
  2. 写请求流程:
    缓存与数据库一致性前言一、缓存是什么?为什么引入缓存?二、缓存一致性总结

    数据更新的时候,是先更新数据库,再删除缓存。

    这样操作的原因在后面会解释。

  3. 缺点:更新频繁的场景会导致缓存频繁的被删除,降低了缓存的作用
  4. 适用场景:用于读较多的场景

read/write through

read/write through的流程和cache aside流程一致,只不过中间多了一个缓存组件。

1.读请求流程:

缓存与数据库一致性前言一、缓存是什么?为什么引入缓存?二、缓存一致性总结

先查询缓存中数据是否存在,如果存在则直接返回,如果不存在,则由缓存组件负责从数据库中同步加载数据。

2.写请求流程:

缓存与数据库一致性前言一、缓存是什么?为什么引入缓存?二、缓存一致性总结

由缓存组件完成数据源和缓存数据的更新。

write behind

write behind与read/write through有很多相似的地方,都是缓存组件来负责缓存和数据库的读写。不同在于,read/write through是同步更新缓存和数据库的,write behind则是只更新缓存,不直接更新数据库,通过批量异步的方式来更新数据库。

1.write behind流程:

缓存与数据库一致性前言一、缓存是什么?为什么引入缓存?二、缓存一致性总结

2.缺点:缓存与数据库的一致性不强。

3.讨论三种数据更新方案

在数据更新的时候,有几种选择,选择是先更新数据库还是先更新缓存,然后就是是删除缓存还是更新缓存,下面分别就这三种情况进行讨论。

先更新数据库,再更新缓存

1.问题

1、线程A更新了数据库

2、线程B更新了数据库

3、线程B更新了缓存

4、线程A更新了缓存

这就出现请求A更新缓存应该比请求B更新缓存早才对,但是因为网络等原因,B却比A更早更新了缓存。这就导致了脏数据,因此不考虑。

2.业务考虑

1、如果是一个写多读少的场景,那么就会导致数据压根没有读到,缓存就被频繁的更新。

2、如果写入数据库的值,并不是直接写入缓存的,而是要经过一系列的计算再写入缓存,那么,每次写入数据库后,都再次计算写入缓存的值。

先删除缓存,再更新数据库

1.问题

同时有一个请求A进行更新操作,另一个请求B进行查询操作,那么会出现这种情况: 1、请求A进行写操作,删除缓存 2、请求B查询发现缓存不存在

3、请求B去查询数据库得到旧值 4、请求B将旧值写入缓存 5、请求A将新值写入数据库

这种其实可以通过设置过期时间解决,如果不设置,可以用延时双删策略

1、先删除缓存

2、再写数据库

3、休眠一秒,再删除缓存。这样就可以把这一秒内产生的脏数据,再次删除。

先更新数据库,再删除缓存

假设这会有两个请求,一个请求A做查询操作,一个请求B做更新操作,那么会有如下情形产生:

1.缓存刚好失效

2.请求A查询数据库,得一个旧值

3.请求B将新值写入数据库

4.请求B删除缓存

5.请求A将查到的旧值写入缓存 ok,如果发生上述情况,确实是会发生脏数据。

然而,发生这种情况的概率又有多少呢?

发生上述情况有一个先天性条件,就是步骤(3)的写数据库操作比步骤(2)的读数据库操作耗时更短,才有可能使得步骤(4)先于步骤(5)。可是,大家想想,数据库的读操作的速度远快于写操作的(不然做读写分离干嘛,做读写分离的意义就是因为读操作比较快,耗资源少),因此步骤(3)耗时比步骤(2)更短,这一情形很难出现。

所以,cache aside采用了先更新数据库,再删除缓存的方案。

总结

以上介绍了三种经典的缓存模式,同时分析了三种数据更新方案存在的问题和优缺点。

继续阅读