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

缓存基础概念与原理

缓存基础概念与原理

这是一个小白的学习记录
边学边练,把踩过的坑都记下来


先说下我为什么要学这个

公司项目最近要搞性能优化,领导让我负责调研缓存方案。拿到手一看,好家伙,缓存这东西天天听,但具体是啥原理,我还真说不清楚。

没办法,只能从基础开始学起,顺便把学习笔记整理出来。

如果你也是新手,希望这篇笔记能帮到你。


什么是缓存?

我当时看到这个概念都懵了,啥是缓存?

说人话就是

缓存就像你买个手机,手机自带个充电器。你每次要充电,直接从抽屉里拿充电器就行,不用每次都去商店买新的。

技术上的解释

缓存就是把经常访问的数据,放在一个访问速度更快的地方。下次再访问同样的数据,就不用去原来的地方(比如数据库)取了,直接从缓存里拿。

我的理解

  • 缓存 = 快速访问的数据副本
  • 核心思想 = 空间换时间
  • 目的 = 减少数据库压力,提高访问速度

为什么需要缓存?

我当时也困惑,数据库不是挺快的吗?为啥还要搞缓存?

后来查资料才明白,主要是这几个原因:

1. 性能问题

数据库查询慢啊!特别是复杂查询、多表关联的时候。

举个例子

无缓存: 用户请求 → 数据库查询(100ms) → 返回结果 有缓存: 用户请求 → 缓存查询(1ms) → 返回结果

好家伙,100 倍的性能差距!

2. 数据库压力

想象一下,如果 1 万个人同时访问同一个商品详情,每次都查数据库,数据库不得崩溃?

就像排队结账

  • 无缓存:1 万个人都去收银台结账,收银员累死
  • 有缓存:收银员把常见商品的价格贴在墙上,大部分人直接看墙就行

3. 成本问题

数据库扩容贵啊!加内存、加 CPU、加机器,哪样不要钱?

缓存便宜多了,用内存换性能,划算!


缓存的工作原理

这个我一开始也没搞懂,后来一点点啃,总算是明白了。

基本流程

没有

用户请求

缓存中有吗?

直接从缓存返回

查询数据库

写入缓存

返回结果

说人话就是

  1. 用户来要数据
  2. 先去缓存里找
  3. 找到了,直接给(命中缓存)
  4. 没找到,去数据库查,查完顺便放缓存里(缓存未命中)
  5. 返回结果

关键概念

命中率

公式

命中率 = 命中缓存的次数 / 总请求次数 * 100%

我的理解

  • 命中率越高,缓存效果越好
  • 一般要达到 80% 以上才算合格
  • 100% 不可能,总有一些冷门数据
淘汰策略

缓存空间有限,满了怎么办?得扔掉一些不用的数据。

常见的淘汰策略

策略说明适用场景
LRU最近最少使用的先淘汰通用场景(推荐)
LFU最不经常使用的先淘汰热点数据明显
FIFO先进先出简单场景
TTL过期时间到了就淘汰时效性数据

我的感受

  • LRU 最常用,效果也最好
  • LFU 适合热点数据明显的场景
  • TTL 适合有过期时间的数据(比如验证码)

缓存的优缺点

这个我得说实话,缓存不是万能的,有优点也有缺点。

优点

  1. 性能提升:访问速度快,响应时间短
  2. 数据库压力小:减少数据库查询次数
  3. 成本低:比数据库扩容便宜
  4. 可扩展性好:可以水平扩展

缺点

  1. 数据一致性问题:缓存和数据库数据可能不一致
  2. 缓存穿透/击穿/雪崩:搞不好会出大问题
  3. 增加复杂度:代码更难维护
  4. 内存占用:缓存要占内存

我的感受

  • 缓存是把双刃剑,用好了性能飞升,用不好问题一堆
  • 一定要想清楚再用,别为了用而用

常见的缓存策略

这个我一开始也懵,什么 Cache-Aside、Read-Through,啥跟啥啊?

后来才明白,就是几种不同的缓存使用方式。

1. Cache-Aside(旁路缓存)

最常用的模式

流程

读操作: 1. 先读缓存 2. 缓存没有,读数据库 3. 把数据写入缓存 4. 返回结果 写操作: 1. 先更新数据库 2. 再删除缓存

代码示例

// 读操作publicUsergetUser(Longid){// 1. 先读缓存Useruser=cache.get(id);if(user!=null){returnuser;}// 2. 缓存没有,读数据库user=userDao.findById(id);// 3. 写入缓存if(user!=null){cache.put(id,user);}// 4. 返回结果returnuser;}// 写操作publicvoidupdateUser(Useruser){// 1. 先更新数据库userDao.update(user);// 2. 再删除缓存cache.delete(user.getId());}

我的感受

  • 最简单,最常用
  • 适合读多写少的场景
  • 我推荐新手就用这个

2. Read-Through(读穿透)

说人话就是

缓存自己负责从数据库加载数据,应用不用管。

流程

1. 应用读缓存 2. 缓存没有,缓存自己从数据库加载 3. 返回结果

代码示例

// 使用 Caffeine 的 CacheLoaderCache<Long,User>cache=Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(10,TimeUnit.MINUTES).build(newCacheLoader<Long,User>(){@OverridepublicUserload(Longid){// 缓存没有时,自动从数据库加载returnuserDao.findById(id);}});// 应用只需要读缓存Useruser=cache.get(id);

我的感受

  • 代码更简洁
  • 适合读多写少的场景
  • 需要缓存库支持(比如 Caffeine)

3. Write-Through(写穿透)

说人话就是

应用只写缓存,缓存自己负责同步到数据库。

流程

1. 应用写缓存 2. 缓存同步写数据库 3. 返回结果

代码示例

// 使用 Caffeine 的 CacheWriterCache<Long,User>cache=Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(10,TimeUnit.MINUTES).writer(newCacheWriter<Long,User>(){@Overridepublicvoidwrite(Longid,Useruser){// 写缓存时,同步写数据库userDao.update(user);}});// 应用只需要写缓存cache.put(id,user);

我的感受

  • 代码更简洁
  • 适合写多读少的场景
  • 需要缓存库支持

4. Write-Behind(写后置)

说人话就是

先写缓存,缓存异步写数据库。

流程

1. 应用写缓存 2. 缓存立即返回成功 3. 缓存异步写数据库

我的感受

  • 性能最好
  • 风险也最大(可能丢数据)
  • 适合对数据一致性要求不高的场景

缓存策略对比

策略优点缺点适用场景
Cache-Aside简单、灵活代码繁琐通用场景(推荐)
Read-Through代码简洁需要缓存库支持读多写少
Write-Through代码简洁、数据一致写性能差写多读少
Write-Behind写性能最好可能丢数据对一致性要求不高

我的建议

  • 新手就用 Cache-Aside,最稳妥
  • 读多写少用 Read-Through
  • 写多读少用 Write-Through
  • Write-Behind 慎用,除非你很清楚风险

验证步骤

1. 理解概念

看完这篇,你应该能回答:

  • 什么是缓存?
  • 为什么需要缓存?
  • 缓存的工作原理是什么?
  • 常见的缓存策略有哪些?

2. 画图理解

自己画一下缓存的流程图,加深理解。

3. 思考场景

想想你的项目中,哪些地方适合用缓存?


总结

我踩过的坑

  1. 一开始不理解缓存策略:后来画图才明白
  2. 以为缓存越快越好:其实要考虑一致性问题
  3. 不知道选哪种策略:新手就用 Cache-Aside

核心要点

  • 缓存是什么:快速访问的数据副本
  • 为什么用缓存:提高性能,减少数据库压力
  • 工作原理:先查缓存,没有再查数据库
  • 常见策略:Cache-Aside(推荐)、Read-Through、Write-Through、Write-Behind

最后说两句

其实缓存基础也没多难,就是多画图,多思考。

我刚开始也懵,后来一点点试,总算是搞定了。

肯定有理解不对的地方,欢迎大佬指正。

如果你也遇到类似问题,希望这篇笔记能帮到你。

http://www.jsqmd.com/news/674083/

相关文章:

  • 吊车地基承载力计算全攻略:从地勘报告到路基箱铺设,一文讲透
  • 基于泰勒展开的YOLOv5通道剪枝重要性评估:理论与实践
  • 面向测试工程师的机器学习调试实战:深入解析损失函数优化
  • 避坑指南:大华海康SDK回调流如何用JavaCV稳定推流到ZLMediaKit?
  • 全球首个龙虾模型:GLM--Turbo(手把手安装、配置、使用教程)来了!
  • Harness 中的推理步数预算:防止无限循环
  • 00华夏之光永存:华为黄大年茶思屋难题揭榜第10期(题目篇)—— 7道云原生核心难题全解析
  • python gitlab-ci
  • 【2026政企采购强制标准】:Blazor离线PWA能力、FIPS 140-2加密集成、GDPR合规审计链——3步通过等保三级验收
  • Godot 4中实现第三人称相机的技巧与实例
  • 模型加载耗时4.2秒?教你用.NET 11 MemoryMappedFile预热+Lazy<T>缓存,在300ms内完成冷启动(已落地券商核心系统)
  • 回归显见:在亚马逊,为何“最简单、最本质”的价值是抵御复杂化陷阱的终极武器
  • CSS如何理解align-content与align-items的区别
  • JavaScript异步编程怎么入门和实践?
  • 笔试训练48天:mari和shiny(动态规划 - 线性dp)
  • 2026指纹浏览器性能优化实战:多开稳定性与资源占用控制全解析
  • 使用 Keepalived 实现高可用
  • YOLOv5-GCNet:融合全局上下文网络的长程依赖建模优化,助力小目标与遮挡场景检测精度提升10%+
  • No idea。。
  • CSS viewport单位在旧移动端支持不佳_利用固定像素值与rem配合
  • YOLO26超市空货架检测系统:单类别识别,mAP50=0.912,推理仅21.6ms(项目源码+数据集+模型权重+UI界面+python+深度学习+远程环境部署)
  • TypeScript 类与 JSON 绑定的艺术
  • 别再死记硬背了!用Python的NumPy库实战CR、LU、QR分解,5分钟搞懂矩阵分解到底在干啥
  • 终极指南:用Meshroom开源工具将普通照片变身高精度3D模型
  • RT-Thread与FreeRTOS线程管理对比:从API差异看设计哲学与实战影响
  • 数字IC面试必刷题:用Verilog实现序列检测器的两种经典方法(状态机 vs. 移位寄存器)
  • 自然语言处理词向量:WordVec与BERT预训练模型对比
  • 用EasyX图形库给你的C语言课设加满分:从贪吃蛇到飞机大战的实战思路
  • Python 模块精讲:hashlib — MD5、SHA 加密(3500 字完整版)
  • 算法训练营第八天|合并两个有序数组