惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Project Zero
Project Zero
GbyAI
GbyAI
Y
Y Combinator Blog
Google DeepMind News
Google DeepMind News
小众软件
小众软件
The GitHub Blog
The GitHub Blog
阮一峰的网络日志
阮一峰的网络日志
J
Java Code Geeks
WordPress大学
WordPress大学
Microsoft Security Blog
Microsoft Security Blog
IT之家
IT之家
F
Fortinet All Blogs
博客园 - 【当耐特】
H
Hackread – Cybersecurity News, Data Breaches, AI and More
P
Proofpoint News Feed
Schneier on Security
Schneier on Security
C
Cybersecurity and Infrastructure Security Agency CISA
L
LINUX DO - 热门话题
Spread Privacy
Spread Privacy
O
OpenAI News
V
V2EX
博客园 - 三生石上(FineUI控件)
AI
AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Stack Overflow Blog
Stack Overflow Blog
P
Privacy International News Feed
Microsoft Azure Blog
Microsoft Azure Blog
Blog — PlanetScale
Blog — PlanetScale
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Jina AI
Jina AI
H
Help Net Security
L
LINUX DO - 最新话题
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tenable Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Cisco Talos Blog
Cisco Talos Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
博客园 - 叶小钗
V
Vulnerabilities – Threatpost
C
Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
酷 壳 – CoolShell
酷 壳 – CoolShell
Hacker News: Ask HN
Hacker News: Ask HN
S
Security @ Cisco Blogs
S
Securelist
T
The Blog of Author Tim Ferriss
Apple Machine Learning Research
Apple Machine Learning Research
美团技术团队
雷峰网
雷峰网
V2EX - 技术
V2EX - 技术

博客园 - 李潘

我们为什么需要分布式系统? 怎么算是在工作中负责? 怎么更好的沟通? 怎么用工作邮件? 为工作排好优先级 聊聊数据压缩 怎么使用硬件同步原语替代锁? 怎么正确使用锁? 为什么Kafka的性能那么好? 怎么避免服务内存溢出? 应用程序之间的通信传输协议 怎么选择数据序列化方案? 怎么利用异步设计提升系统性能? 怎么处理消息积压问题? 怎么处理消息重发的问题? 怎么做才能不丢消息? 怎么用消息队列实现分布式事务? 聊聊消息队列中的基础概念 我们要选择哪个消息队列产品?
聊聊缓存
李潘 · 2023-03-17 · via 博客园 - 李潘

在设计高并发、高性能的系统架构时,缓存是绕不开的一个话题,之所以用缓存,是因为不同的存储介质的访问速度存在巨大差异,例如SSD(固态硬盘)每秒钟可以读写几千次,而内存的随机读写速度是SSD的10万倍。使用内存作为缓存来加速应用程序的访问速度,是几乎所有高性能系统都会采用的方法。

缓存的思想很简单:把低速存储的数据,复制一份放到高速存储中,用来加速数据访问。

缓存的分类

缓存主要分为两大类:

  • 读写缓存
  • 只读缓存

这两类缓存的区别在于更新数据的时候是否经过缓存。

读写缓存

Kafka使用的PageCache就是典型的读写缓存。操作系统会利用系统空闲的物理内存来给文件读写做缓存,应用程序在写文件的时候,操作系统会先把数据写入到PageCache中,数据在成功写到PageCache之后,对于用户代码来说,写入就结束嘞。操作系统再通过异步的方式将数据更新到磁盘的文件中。应用程序在读文件的时候,操作系统也是先尝试从PageCache中寻找数据,如果找到就直接返回数据,找不到就会触发一个缺页中断,然后操作系统把数据从文件读取到PageCache中,再返回给应用程序。

我么可以看到写数据时,并不是同时将数据写到PageCache和磁盘上,这中间会有一个延迟。操作系统可以保证,即使是应用程序意外退出了,操作系统也会把这部分数据同步到磁盘上,但是如果服务器突然掉电了,这部分数据就会丢失。

读写缓存这种设计,天然就不是可靠的,这是一种牺牲数据一致性换取性能的设计。

写缓存的实现是非常复杂的,应用恒旭不停地更新PageCache中的数据,操作系统需要记录哪些数据有变化,同时还要在另外一个线程中,把缓存中变化的数据更新到磁盘中。在提供并发读写的同时来异步更新数据,这个过程中要保证数据的一致性,并且有非常好的性能,很不容易。

Kafka为什么可以使用PageCache提升性能?

Kafka可以使用PageCache并取得性能提升,有三个原因:

  1. 消息队列中数据的读写比例基本是1:1,我们用消息队列发送的大部分数据都是一收一发的。
  2. Kafka不是靠磁盘来保证数据的可靠性,它更依赖于不同节点上的多副本来解决数据可靠性问题。
  3. PageCache的读写缓存是操作系统实现的,Kafka只需要按照正确的方法来使用就可以了,不会涉及到实现复杂度的问题。

对于不同的使用场景,我们选择缓存的方式也有区别,如果读次数是写次数的几倍到几十倍,那么可以选择只读缓存,如果数据的读写次数基本一致,那么可以选择读写缓存。

只读缓存

对于只读缓存来说,我们需要考虑一个问题:缓存的数据来源于磁盘,那么应该怎么更新缓存中的数据呢?

我们可以有三种方法来更新只读缓存的数据:

  1. 数据更新时,同时更新磁盘和缓存,这种方法可能会带来数据不一致的问题,例如我们是选择同步还是异步来更新缓存?如果同步更新,磁盘更新成功了,缓存更新失败嘞,需要反复重试来保证更新成功吗?如果多次重试都失败,那么这次更新算成功还是失败呢?如果是异步更新缓存,怎么保证更新的时序?
  2. 定时将磁盘上的数据同步到缓存中,同步时可以采用全量更新,也可以选择增量更新。这种方法的缺点是缓存更新不会很及时,优点是实现起来非常简单。
  3. 我们不去更新缓存中的数据,而是给缓存中的每条数据设置一个比较短的过期时间,数据过期以后即使它还在缓存中,我们也会认为它不再有效,需要从磁盘中再次加载,这样就实现了数据更新。

缓存置换策略

当应用程序要访问某些数据时,如果这些数据在缓存中,那么直接访问缓存中的数据就可以了,这种情况我们称为一次缓存命中;如果数据不在缓存中,那只能去磁盘访问数据,我们称为缓存穿透

一般来说我们都会在数据首次被访问时,把这条数据放到缓存中,随着访问的数据越来越多,缓存空间会被占完,这时就需要把缓存中的一些数据删掉,以便存放新的数据,这个过程称为缓存置换

我们有两种缓存置换思路:

  1. 根据业务逻辑,定制化缓存置换策略。例如,当我们知道某些数据已经被删了,永远不会再访问到,那么优先置换这些数据是没有问题的。
  2. 使用通用的置换算法,例如LRU算法, 也称为最近最少使用算法,它的思想是最近刚刚被访问到的数据,它在将来被访问的可能性也很大,而很久都没有被访问过的数据,未来再被访问的几率也不大。