当前位置: 首页 > news >正文

缓存的思考与总结

缓存的思考与总结

    • 什么是缓存
    • 缓存命中率
    • 数据一致性
      • 旁路模式 Cache aside
      • 双写模式
        • 直写模式 write through
        • 异步写 Write Behind
      • 旁路和双写
    • 案例

新技术或中间的引入,一定是解决了亟待解决的问题或是显著提升了系统性能,并且这种改变所带来的增幅,可以让我们忽略引入新技术所带来的系统复杂性等负面影响。好比是CAP理论中对C和A的权衡。

什么是缓存

从内存中读取数据,从文件系统通过IO读取磁盘数据,两者在时间上存在较大差异,毫无疑问,从内存中读取数据相较于磁盘会更快,于是便有了缓存,很典型的以空间换时间的运用。
如果数据都放在内存中自然是最好不过,也就没有缓存这么一说了。但目前来看,内存依旧属于紧张资源,需要有选择的将数据(读多写少)放入内存作为缓存来使用。
在缓存方面,大多数问题都可以归结到以下两个方面去讨论:

  • 缓存命中率
  • 数据一致性

缓存命中率

读取数据时,从缓存中是否读取到了数据。系统在设计添加缓存层时,一方面提升系统响应速度,另一方面也能拦截部分查询请求从而分担数据库压力。对于一些我们希望在缓存层被拦截的请求,如果缓存没有命中,那么缓存层将失去意义。比如常说的几个概念:

  • 缓存穿透:查询缓存中不存在的键(非法的key),请求到达DB层
  • 缓存击穿:查询缓存中不存在的键(尚未构建缓存),请求到达DB层
  • 缓存雪崩:缓存大面积失效,请求到达DB层

你会发现,上述都是在描述一个概念:缓存命中率。只不过是不同场景下缓存未命中的情况:
我们希望请求在缓存层被拦截,但由于未命中缓存导致请求到达DB层。所以上述场景的解决方案也都是围绕着:如何提高缓存命中率 来展开的。
缓存击穿还涉及并发场景下缓存重建问题,需要通过加锁来避免。

数据一致性

有了缓存,就意味着有了两份数据,DB层一份,内存一份,那就必然会涉及数据一致性的问题,并且由于是两份数据,数据同步期间必然会存在不一致情况,除非将数据的修改和缓存的修改作为一个原子操作(单体应用)。所以,不能仅仅要设计数据如何读取提高命中率,还要设计数据更新时的策略。也就是说,加入缓存层后,要从读写两方面进行约束,形成闭环,这样才能保证缓存和DB层的数据一致性。
常见的场景必然有成熟的解决方案,对于缓存的数据一致性问题,常见的设计有以下几种:

  • 旁路模式
  • 直写模式
  • 异步写模式

旁路模式 Cache aside

读取时先从缓存中读取数据。如果缓存中没有数据,则从数据库中读取,并将数据写入缓存。更新数据时,先更新数据库,然后再将缓存中的数据失效。

旁路模式中,并发场景下,先更新数据后再删除缓存先删除缓存再更新数据两者有所不同,即使双删策略保证第二次删除后读取到的都是新数据。
推荐使用先更新数据库再删除缓存的做法,优点是不存在使用旧数据重建缓存的情况,且数据不一致的窗口期不依赖于第二次删除,也就是说:更新数据后删除缓存前,并发读会读到旧缓存,但更新数据且删除缓存后,不一致窗口期便结束了。假如设定的第二次删除的延时是1小时,先更新数据库再删除缓存这种方式会在删除缓存后结束数据不一致;但先删除缓存再更新数据的方式则强依赖与第二次删除,会在1小时后才结束数据不一致。

双写模式

顾名思义,既写数据库,又写缓存。包括直写和异步写(严格的双写是指一份数据同时写入两种存储介质,这里的异步写归结到双写模式下便于记忆)

直写模式 write through

读取时先从缓存中读取数据,在写入数据时,数据同时写入缓存和数据库

异步写 Write Behind

写入数据时,数据只更新缓存,并异步批量刷新到数据库中

旁路和双写

为了方便理解和区分旁路和双写模式,最简单的区分就是:
旁路模式中,缓存更新(失效重建)是被动的,由后续的读操作进行缓存重建;而双写模式则是主动更新缓存

旁路模式和双写模式是保证缓存和DB数据一致性的常用做法。分布式系统中也存在一些在旁路和双写基础上进行改进增强的设计,比如旁路模式+TTL过期时间,双写+补偿机制等,用来处理缓存操作失败时的场景,感兴趣的可以继续研究。

案例

写这篇偏总结性的文章,起因是之前写的一个IDEA插件 ,在第一版设计的时候,考虑到之后插件记录的数据增多,于是通过代理模式预留了扩展。
在这里插入图片描述
这里通过代理模式添加缓存层,在代理对象中统一进行缓存的处理。
在这里插入图片描述
由于插件开发是单体应用且不考虑多线程场景(多编辑器同时操作一个源文件时,会提示并拒绝),所以这里我们使用旁路模式实现最简单的LRU缓存。具体怎么做呢?

  1. 读操作优先读缓存,缓存没有再读DB(这里是持久化文件)
  2. 数据一旦发生修改,例如修改了高亮记录中的笔记内容,则失效缓存,等待下一次读取时重新构建缓存即可。
    但是有个问题:这里使用的缓存key和value,key是源码文件名称,value是该文件所有的高亮笔记。这里缓存的粒度很大,如果每次修改一条高亮笔记就重建整个源码文件的数据,不是很合理。所以还是双写模式更适合,避免牵一发动全身。每次更新笔记,只更新缓存中笔记列表里面相同ID的缓存记录即可。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 《拿下奇怪的前端报错》:nvm不可用报错`GLIBC_2.27‘‘GLIBCXX_3.4.20‘not Found?+ 使用docker构建多个前端项目实践
  • 计算机网络:概述 --- 体系结构
  • ML 系列:机器学习和深度学习的深层次总结(08)—欠拟合、过拟合,正确拟合
  • QT中添加资源文件(一看就会)
  • 开源实时多模态AI聊天机器人Moshi,语音对话延迟低至200毫秒!
  • MySQL面试题——第一篇
  • 信息学奥赛:青少年编程的高光舞台,通向未来科技的敲门砖
  • text2sql(NL2Sql)综述《The Dawn of Natural Language to SQL: Are We Fully Ready?》
  • 【游戏引擎】C++自制游戏引擎 Lunar Game Engine
  • spring与springmvc整合
  • Stable Diffusion 使用详解(13)--- 3D纹理增强
  • 【Qt笔记】QTabWidget控件详解
  • 【我的 PWN 学习手札】House of Botcake —— tcache key 绕过
  • 量化交易----数据透视表----融资融券优惠代码
  • 前端三大框架 Vue、React 和 Angular 的市场占比分析
  • 【干货分享】SpringCloud微服务架构分布式组件如何共享session对象
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • ECMAScript入门(七)--Module语法
  • Flannel解读
  • Fundebug计费标准解释:事件数是如何定义的?
  • JavaSE小实践1:Java爬取斗图网站的所有表情包
  • node-glob通配符
  • React Transition Group -- Transition 组件
  • webgl (原生)基础入门指南【一】
  • weex踩坑之旅第一弹 ~ 搭建具有入口文件的weex脚手架
  • 爱情 北京女病人
  • 配置 PM2 实现代码自动发布
  • 删除表内多余的重复数据
  • 体验javascript之美-第五课 匿名函数自执行和闭包是一回事儿吗?
  • 【干货分享】dos命令大全
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • 正则表达式-基础知识Review
  • ​字​节​一​面​
  • # SpringBoot 如何让指定的Bean先加载
  • $refs 、$nextTic、动态组件、name的使用
  • (6)STL算法之转换
  • (python)数据结构---字典
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • (超详细)2-YOLOV5改进-添加SimAM注意力机制
  • (二)原生js案例之数码时钟计时
  • (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
  • (免费领源码)Java#Springboot#mysql农产品销售管理系统47627-计算机毕业设计项目选题推荐
  • (十一)手动添加用户和文件的特殊权限
  • (学习日记)2024.04.10:UCOSIII第三十八节:事件实验
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (一)认识微服务
  • (转) Android中ViewStub组件使用
  • (转)EXC_BREAKPOINT僵尸错误
  • (转)Linq学习笔记
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • ***检测工具之RKHunter AIDE
  • .describe() python_Python-Win32com-Excel
  • .net core 的缓存方案
  • .Net Core 生成管理员权限的应用程序
  • .Net Core/.Net6/.Net8 ,启动配置/Program.cs 配置