首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >缓存、数据库一致性问题

缓存、数据库一致性问题

作者头像
Joseph_青椒
修改2023-08-02 18:28:16
修改2023-08-02 18:28:16
6410
举报
文章被收录于专栏:java_josephjava_joseph
这里和大家分享一下,对缓存、数据库一致性问题问题

先学一下,缓存与数据库的读写顺序

Redis缓存读写的三种模式

Cache Aside 读写分离模式 / Read/Write Through 读写穿透模式 / Write Back 异步写入模式

CacheAside 旁路缓存模式

读操作: 先查询缓存,有则直接返回,没有则查询数据库再放到缓存

写操作:写到数据库,值得注意的是,写操作通常需要我们直接删除缓存,因为若是hash、list结构,需要遍历去修改数据,直接删除缓存,读数据直接从数据库拿过来就好了:

优缺点对比:

优点:读取效率高,缓存命中率高,写操作直接写数据库,数据一致性强。

缺点:缓存、数据库 存在数据不一致的问题

场景:适用于读多写少的场景,比如电商平台的商品详情页,页面出现一些脏数据。

下面讲解Redis更新数据库、缓存顺序问题会再次提到。

这种模式删除缓存,下面两种模式是更新缓存

Read/Write Through 读写穿透模式

读写穿透模式,是将数据的更新和读取都上层到缓存层面,读写操作都会操作缓存,应用程序对于数据库是透明的,

也就是说,应用程序并不知道数据库的存在,缓存进行更新到数据库和从数据库读取数据!

这种策略实现较为复杂,一般少用

优缺点:

优点:写操作快,一致性高,缓存与数据库保持一致,缓存命中率高

缺点:读操作慢,因为读数据不在缓存的话,还是要从数据库中捞取数据,

场景:适合读少写多的场景,比如云存储Ceph

Write Behind 异步写入模式

这种模式是写操作很快的,但是同步到数据库是延迟进行的,一般会将数据落到磁盘中,等有读操作的时候,再异步批量更新的数据库,读取的时候也直接在缓存,这种模式读写操作很快,并且不会频繁的更新数据库,性能高

优点:显然是读写操作快,性能高,且数据一致性高

缺点:造成数据的丢失

应用场景:适用于读操作很少,但是写操作很多的场景,比如游戏中用户积分等信息,打游戏一般查看很少,但是一直在进行写入。还有就是点赞量、文章访问量,允许丢失,但是要速度。

注意:选择何种模式要结合业务场景,缓存这个一般来说读写分离模式是居多的,下面我也对这种模式进行剖析。

Redis更新数据库、缓存的顺序问题。

在并发量不是很大的情况下,一般是没什么问题的,但是这里都用到缓存了,并发问题还是需要我们去着重考虑的

在高并发业务下,先更新缓存,还是先更新数据库,就会出现缓存中出现脏数据,或者说缓存、数据库不一致的现象

这里我们说的是读写分离的情况,这里的话可能有些纰漏,Cache Aside专门指的是,先写数据库、再删除缓存的情况。

但是不影响我们下面的分析。

我们要探讨的是,先操作数据库还是缓存,更新缓存还是删除缓存

这里给出三种策略

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

在CacheAside读写分离模式介绍的时候,说到,写操作,是尽量去删除缓存而不是去删除,为何这样做呢?现在分析一下

在不考虑一致性的情况下,更新是会存在很大的开销的,比如list,hash,需要去遍历再修改,会很占用开销,浪费时间,即使是String,也可能是一个Json字符串,或者一个map,

比如,我们通过缓存进行扣减库存,需要查出订单模式的整个字段,进行反序列化之后,再解析库存字段,修改,再序列化,更新到缓存,这样开销是很大的,且这样遍历出错的可能性也很大

对于一致性问题,更新缓存这种策略会存在,如

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

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

这里有点小纰漏。将事物改为线程

在写写并发中,更新缓存,上面两种情况,都出现了数据不一致的情况,可见,更新缓存相对于删除缓存,既复杂,还有不一致的问题,所以要删除而不是更新。

值得注意的是,这种删除策略,导致的cache miss,可能会发生缓存击穿的问题,这种问题是可以通过加锁来缓解的。

好,现在我们的问题就变成了,是先删除缓存,再更新数据库,还是先更新数据库,再删除缓存。

这里为了方便下面讲解,直接告诉两个比较合适的策略,

策略1:先写数据库,再删缓存 策略 2 先删缓存,再删数据库,再删缓存(延迟双删)

场景:并发量较小条件下,使用策略1.并发量较大,使用策略2

这是我总结的规律,

下面, 看原因

对于先写数据库,再删缓存,因为是删除,所以不会存在更新缓存那也的写写并发问题,但是会出现读写并发问题。

这里先带大家理顺一个问题,读操作的时候,命中不到缓存,会从数据库中读取,再写到缓存,那么能命中到缓存的话,会更新缓存吗?

答案是不会的,这样做是为了尽可能保证缓存中数据的准确一致,但是删除策略,会保证下一次重新读数据库到缓存是最新的,所以没必要命中缓存的情况下,继续更新了。

所以一个读线程是这样操作的

可见,虽然读线程不会写数据库,但是还是会写缓存的。

那么先写数据库,再写缓存读写并发问题什么呢?这个发生是建立在缓存没命中,为何这样说?看图

可见,这种情况下,是未命中缓存的,这样会出现不一致的问题。

当然,这种情况下,也是在并发量很小的情况下才会发生的,这个先写数据库再写缓存,就是我们所说的cache aside pattern设计模式。

那么,我们将更新筛选出去,还剩下两种策略,先写数据库,再删缓存,;;先删缓存,再写数据库,

为何给出的两个标准策略,没有先删缓存,再删数据库吗?这里的话是因为把cache aside中的bug,给放大了,这个bug的条件是没命中缓存,当先删除缓存的话,这种情况会变的很多,所以不采用这个策略。

现在第一个策略,先写数据库,再删缓存 cache Aside设计模式已经搞定了,现在我们讨论延时双删策略。

这里我阅览博文中,发现是延时双删是建立在先删缓存再写数据库的情况下的,另外还提到了分布式锁。but,我觉得要分业务,只要延时双删都是可以的,

针对前面的读写并发问题中,看下图:

我个人认为是都可以的,后续我遇到高人点拔,会同步到这篇文章,包括分布式锁的优化。

这里再给大家分享一个常见的场景方案

场景:读多写少,且不要求强一致性,允许中间状态

这种方案在命中不到缓存的情况下,不再向数据库请求数据,而是访问本地缓存中的老数据,命中的话直接返回,并且将数据存放到本地缓存中,写的时候直接写数据库就可以,不用考虑删除问题,也不会出现并发问题。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-07-15T,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Redis缓存读写的三种模式
    • CacheAside 旁路缓存模式
    • Read/Write Through 读写穿透模式
    • Write Behind 异步写入模式
  • Redis更新数据库、缓存的顺序问题。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档