首页 > 其他分享 >老话新谈之缓存一致性

老话新谈之缓存一致性

时间:2023-06-10 17:46:25浏览次数:74  
标签:缓存 老话 数据库 Cache DB 更新 新谈 data

前言

缓存一致性常见的更新策略也比较多,如先更新数据库再更新缓存,先删缓存再更新数据库等等,我在理解的时候有些混乱,所以这个文章提供了一些理解上的技巧去理解缓存一致性。

为什么会有缓存一致性的问题

  1. 缓存与数据库是两套中间件,存在网络抖动之类的原因导致没有更新任一方的可能
  2. 数据库大多都是事务型的中间件,支持错误回滚,缓存大多是非事务型的中间件,这里缓存更新失败了没办法回滚

所以根因是缓存大部分不支持事务无法回滚。

怎么尽量解决缓存一致性的问题

操作二者必定有先后顺序,存在以下两个情况:

  1. 先操作缓存,再操作数据库。操作缓存成功,数据库更新失败,缓存无法回滚,数据不一致
  2. 先操作数据库,再操作缓存。操作数据库成功,缓存操作失败,可触发异常回滚数据库,数据一致

根据上述所列,只能先操作数据库,再操作缓存了。

操作缓存也分两种:

  1. 更新缓存数据,可能并发请求,后一请求更新缓存的数据被前一请求的更新覆盖了,导致数据不一致
  2. 删除缓存数据,并发请求,二者都使缓存失效,查询请求将数据库数据加载到缓存中,数据一致

根据上述所列,只能使缓存失效,查询请求加载数据到缓存中了。

所以,如果在不加任何重试措施的情况下,先操作数据库,再删除缓存是一个容错较好的方法。

缓存一致性的分类 & 存在的问题

Client 维护缓存 & 数据库的一致性

  1. 更新缓存 -> 更新数据库

    图片1

@startuml
Database Database   as DB
entity   Cache      as Cache

transaction1 -> Cache: update data
transaction1 <-- Cache: update result

transaction1 -> DB: update data
transaction1 <-- DB: update result

@enduml
  • 可能出现的数据不一致

​ 数据不一致:更新缓存成功了,更新数据库失败了,有数据不一致的问题,直到缓存超时失效或又一更新请求操作成功都会不一致

  • 改进方式

    若保证更新数据仅有少数的服务更新,可以将更新数据库请求入队处理,且可加入重试机制。但是队列的加入会增大系统复杂度,并且重试以及缓存更新顺序不一致会加剧数据不一致

  1. 更新数据库 -> 更新缓存

@startuml
Database Database   as DB
entity   Cache      as Cache

transaction1 -> DB: update data
transaction1 <-- DB: update result

transaction2 -> DB: update data
transaction2 <-- DB: update result


transaction2 -> Cache: update data
transaction2 <-- Cache: update result

transaction1 -> Cache: update data
transaction1 <-- Cache: update result

@enduml
  • 可能出现的数据不一致

​ 数据不一致:如 t1 先更新数据库,t2 在 t1 更新缓存前把数据库缓存都更新完了,t1 再更新缓存,这时候缓存上是 t1 的数据,数据库是 t2 的数据

  • 改进方式

    若保证更新数据仅有少数的服务更新,可以将更新数据库请求入队处理,但是队列更新的引入增大了系统复杂度

  1. 删除缓存 -> 更新数据库

@startuml
Database Database   as DB
entity   Cache      as Cache

transaction1 -> Cache: delete data

query1 -> DB: select data
query1 -> Cache: insert data

transaction1 -> DB: update result

@enduml
  • 可能出现的数据不一致

    1. 如图所示,更新请求先删除缓存,查询请求从缓存获取不到数据从数据库获取数据(老数据)加载到缓存中,更新请求更新数据库
    2. 这样的流程会导致查询请求加载老数据到缓存中,后续更新请求更新新数据到数据库中,导致数据不一致
  • 改进方式

    暂无。

  1. 更新数据库 -> 删除缓存

@startuml
Database Database   as DB
entity   Cache      as Cache

query1 -> DB: select data
transaction1 -> DB: update result
transaction1 -> Cache: delete data
query1 -> Cache: insert data

@enduml
  • 可能出现的数据不一致

    查询请求先拿到数据,在插入缓存前更新请求进来更新数据库并使缓存失效,这个请求比较罕见

    1. 发生的场景
      1. 查询请求所在机器请求缓存比更新请求做完的整个流程都要慢
    2. 发生的概率
      1. 很低。因为操作缓存一般会比操作数据库要快
  • 改进方式

    1. 变更数据记录变更事件
      1. 步骤
        1. 更新数据同步记录一个事件在本地内存中
        2. 查询请求在插入缓存前查询事件,如果存在变更则查数据库获取最新数据
        3. 如果此数据在查询请求插入缓存过程中一直变更,这里需要先返回当前数据库结果给上游,再开异步任务轮训事件/数据库插入缓存
      2. 适用场景
        1. 只适用单节点

Server 维护缓存 & 数据库的一致性

  1. Read though/Write though

    • read though

    @startuml
    Database Database   as DB
    entity   Cache      as Cache
    
    query -> repository: select data
    
    repository -> cache: get data
    repository -> DB: get data
    DB -> repository: return data
    repository -> cache: update data
    repository -> query: return data
    
    @enduml
    
    • wirte though

    @startuml
    Database Database   as DB
    entity   Cache      as Cache
    
    transcation -> repository: update data
    
    repository -> cache: update data
    repository -> DB: update data
    DB -> repository: return result
    repository -> transcation: return result
    
    @enduml
    
  • 可能出现的数据不一致
    • 程序没有优雅关闭,更新请求先更新了缓存,但还没更新数据库,数据丢失
    • 更新缓存成功,更新数据库失败导致的数据不一致
  • 适用场景
    • 更新数据库极低概率失败
    • 程序有优雅关闭功能
  • 改进方式
    • 暂无
  1. Write Behind

@startuml
Database Database   as DB
entity   Cache      as Cache

query -> repository: query data

repository -> cache: query data
repository -> DB: query data
DB -> repository: return data
repository -> cache: update data

repository -> query: return data

@enduml

@startuml
Database Database   as DB
entity   Cache      as Cache

transcation -> repository: update data

repository -> cache: update data

repository -> DB: batch update data

@enduml
  • 可能出现的数据不一致
    • 程序没有优雅关闭,更新请求先更新了缓存,但还没更新数据库,数据丢失
    • 批量更新数据库失败导致的数据不一致
  • 适用场景
    • 更新数据库极低概率失败
    • 程序有优雅关闭功能
  • 改进方式
    • 暂无

参考

https://coolshell.cn/articles/17416.html

本文首发于cartoon的博客

转载请注明出处:https://cartoonyu.github.io

标签:缓存,老话,数据库,Cache,DB,更新,新谈,data
From: https://www.cnblogs.com/cartooon/p/17471633.html

相关文章

  • vue解决后端返回的图片路径名和原图片路径名相同时,浏览器缓存只会加载首次缓存的图片
    方法一:让后端更改返回的路径名字。方法二:前端自己处理。在图片路径后拼接随机数或者时间戳我用的是时间戳this.$refs.rotate.src=res.data.url+"?"+newDate().getTime() :src="formData.indexUrl+'?'+newDate().getTime()"......
  • Spring中的三级缓存
    Spring中的三级缓存是针对bean的加载和创建过程而设计的。在Spring容器中,每次请求获取一个bean时,Spring首先检查一级缓存、二级缓存和三级缓存中是否已经存在该bean,如果存在,则直接返回缓存中的实例对象,否则才进行bean的创建。三级缓存包括:singletonObjects缓存:一级......
  • HTTP的缓存机制是什么?
    HTTP缓存机制作为一项重要技术,能够提高网页加载速度和节省网络流量。那它的缓存的机制是什么?今天我们就来说说。一、HTTP的缓存机制是什么?1.客户端请求资源当我们在浏览器中输入网址或点击链接时,浏览器会向服务器发出HTTP请求,请求特定的资源,如网页、图像或脚本文件。2.服务器响应......
  • Cache - 直接映射缓存
    参考https://zhuanlan.zhihu.com/p/1022934371.Cachelinecachesize:cache可以缓存最大数据的大小。将cache均分相等的块,每一块称为cacheline,现在的硬件设计中,一般cacheline的大小为4-128字节,cacheline做的太小会导致tag资源占用过大。cacheline是cache和主存......
  • 浏览器存储和浏览器缓存的区别?
    浏览器存储和浏览器缓存是两个不同的概念,其主要区别如下:数据类型不同浏览器存储可以用来存储各种类型的数据,包括字符串、数字、布尔值、对象、数组等等。而浏览器缓存则主要用于缓存已经访问过的资源文件,例如HTML、CSS、JavaScript、图片、音视频等。存储位置不同浏览器......
  • 【缓存】J2Cache —— 基于内存和 Redis 的两级 Java 缓存框架的使用方法
    本文目录一、J2Cache简介二、数据读取三、使用方法及实际示例3.1引用Maven3.2准备配置3.3编写代码3.3.1Demo示例3.3.2实际案例四、常见问题4.1J2Cache的使用场景是什么?4.2为什么不能在程序中设置缓存的有效期4.3如何使用memcached作为二级缓存4.4使用何种Redis的存......
  • 深入浅出Spring原理及实战「缓存Cache开发系列」
    1.  缓存Cache的概念和作用在现代软件开发中,缓存已经成为了一个非常重要的概念。缓存是指将数据存储在一个临时的存储器中,以便于快速访问和读取。缓存的作用是提高系统的性能和响应速度,减少网络流量和数据库的负载。以电影院购票为例,当用户选择一部电影时,系统需要查询电影的......
  • mybatis的缓存
    1. 什么是缓存  113缓存:cache1.1 缓存的作⽤:113通过减少IO的⽅式,来提⾼程序的执⾏效率。1.2 mybatis的缓存:113将select语句的查询结果放到缓存(内存)当中,下⼀次还是这条select语句的话,直接从缓存中取,不再查数据库。⼀⽅⾯是减少了IO。另⼀⽅⾯不再执⾏繁琐的查找算法。效率⼤⼤......
  • springMVC集成缓存框架Ehcache
    概述Ehcache算是当前比较流行的缓存框架,使用缓存可以极大的缓解服务器和数据库的压力,提高访问效率,提高服务器的并发能力。接下来我们看怎么把缓存在springmvc种使用起来。详细Ehcache算是当前比较流行的缓存框架,使用缓存可以极大的缓解服务器和数据库的压力,提高访问......
  • 十二、缓存穿透、缓存击穿、缓存雪崩
    缓存穿透缓存穿透是指查询一个缓存和数据库中都不存在的数据,客户端不断发起请求,导致数据库压力过大解决方法1、采用布隆过滤器,将所有可能存在的数据,哈希到一个很大的bitmap中,一个一定不存在的数据会被bitmap拦截调,从而避免了对数据库的查询压力。2、如果查询的数据为空,直......