发布于:2020-12-31 14:11:02
0
159
0
所以…缓存。它是什么?这是一种快速获得收益的方法,无需反复计算或获取内容,从而获得性能和成本优势。这也是它的名字来源,它是“cache -ching!”收银机的声音来自2014年的黑暗时代,那时还没有Apple Pay,实物货币还很流行。我现在是父亲了,接受现实吧。
假设我们需要调用一个API或查询数据库服务器,或者只是取一大堆数字(谷歌表示这是一个实际的单词,我检查过了),然后把它们加起来。这些都相当昂贵。所以我们缓存结果——我们让它便于重用。
我们为什么要缓存?
我认为讨论这个问题在这里重要的 是多么昂贵 上面的一些东西。现代计算机中已经有几层缓存在起作用。作为一个具体的例子,我们将使用我们的一个Web服务器,该服务器目前装有一对 Intel Xeon E5-2960 v3 CPU 和2133MHz DIMM。高速缓存访问是处理器的“多少个周期”功能,因此通过知道我们始终以3.06GHz(性能功耗模式)运行,我们可以得出延迟(此处为Intel体系结构参考 –这些处理器属于Haswell一代):
L1(每个内核):4个周期或 〜1.3ns 延迟– 12x 32KB + 32KB
L2(每个内核):12个周期或 〜3.92ns 延迟– 12x 256KB
L3(共享):34个周期或 〜11.11ns 延迟– 30MB
系统内存: 〜100ns 延迟– 8倍8GB
每个缓存层都可以存储更多,但是距离更远。在处理器设计中要权衡取舍。例如,平均每个内核更多的内存意味着(几乎可以肯定)意味着将其与内核之间的距离更远,这会增加延迟,机会成本和功耗。在此范围内,电子必须行进多远会产生重大影响。请记住,距离 每秒乘以 数十亿。
而且我没有涉及上面的磁盘延迟,因为我们很少接触磁盘。为什么?好吧,我想解释一下,我们需要……看一下磁盘。哦,闪闪发光的磁盘!但请不要在袜子跑动后触摸它们。在Stack Overflow上,任何不是备份或日志记录服务器的产品都位于SSD上。本地存储通常分为以下几层:
NVMe SSD:〜120μs(来源)
SATA或SAS SSD:〜400–600μs(来源)
旋转硬盘:2–6ms(源)
这些数字一直在变化,所以不要过多地关注确切的数字。我们试图评估的是这些存储层之间差异的大小。让我们看一下列表(假设每个数字的下限,这是最好的数字):
L1:1.3ns
L2:3.92ns(慢3倍)
L3:11.11ns(慢8.5倍)
DDR4 RAM:100ns(慢77倍)
NVMe SSD:120,000ns(慢92,307倍)
SATA / SAS SSD:400,000ns(慢307,692x)
旋转硬盘:2–6ms(慢1,538,461x)
Microsoft Live登录:12次重定向和5秒(慢3,846,153,846倍,大约)
如果数字不是您的事, 这是Colin Scott提供的简洁的开源可视化效果 (使用滑块!) (您甚至可以查看它们随着时间的推移是如何演变的-确实很简洁):
考虑到这些绩效数字并考虑规模感,让我们添加一些每天都很重要的数字。假设我们的数据源是 X, X 无关紧要。它可能是SQL,或者是微服务,或者是宏服务,或者是左垫服务,或者是Redis,或者是磁盘上的文件等。这里的关键是我们正在将源的性能与RAM的性能进行比较。假设我们的来源需要……
100ns(从RAM –快速!)
1ms(慢10,000倍)
100ms(慢100,000倍)
1秒(慢1,000,000倍)
我不认为我们需要去进一步说明这一点: 即只需要1毫秒,即使事情的方式, 方法 比本地内存要慢。请记住:毫秒,微秒,纳秒–以防万一其他人忘记了1000ns!= 1ms,就像我有时做的那样……
但是并不是所有的缓存都是本地的。例如,我们使用Redis进行Web层背后的共享缓存(我们将在稍后介绍))。假设我们要遍及整个网络来获取它。对于我们来说,这是一个0.17毫秒的往返,您还需要发送一些数据。对于小物件(通常),总共约为0.2-0.5ms。仍然比本地RAM慢2000–5,000倍,但也比大多数来源快很多。请记住,这些数字是因为我们位于小型局域网中。云延迟通常会更高,因此请查看您的延迟。
当我们获得数据时,也许我们也想以某种方式对其进行按摩。可能是瑞典人。也许我们需要总计,也许我们需要过滤,也许我们需要对其进行编码,也许我们需要随机捏弄它以欺骗您。那是一项测试,看您是否还在阅读。你通过了!不管原因是什么,通用性通常是 我们想做 <x> 一次,而不是 每次我们服务时。
有时我们节省延迟,有时我们节省CPU。通常,其中一个或两个都是为什么要引入缓存。现在,让我们涵盖另一面...
为什么我们不缓存?
对于每个讨厌缓存的人,这是给您的部分!是的,我完全扮演双方的角色。
鉴于上述情况以及获胜的激烈程度, 我们为什么 不缓存一些东西?好吧,因为 每个决策都需要权衡取舍。每一个 单。一。它可以很简单,只需花费时间或机会成本,但仍然需要权衡取舍。
当涉及到缓存时,添加缓存会带来一些成本:
在需要时以及在需要时清除值(缓存无效- 我们将在少数几个中介绍)
缓存使用的内存
访问缓存的延迟(权衡了对源的访问)
花费更多的时间和精力在调试更复杂的事情上
每当出现缓存候选对象(通常具有新功能)时,我们都需要评估这些内容……这并不总是一件容易的事。尽管缓存是一门精确的科学,就像占星术一样,它仍然很棘手。
在Stack Overflow,我们的体系结构具有一个总体主题:使其尽可能简单。简单易于评估,推理,调试和根据需要进行更改。仅在需要时和更复杂的情况 下使其更复杂。这包括缓存。仅在需要时缓存。它增加了更多的工作并 增加了错误的机会,因此除非需要,否则:不需要。至少还没有。
让我们开始问一些问题。
命中缓存快得多吗?
我们要保存什么?
值得储存吗?
是否值得清理上述存储(例如垃圾收集)?
它会立即在大对象堆上继续吗?
我们必须多久使它失效一次?
我们认为每个缓存条目会有多少次点击?
它会与其他使无效化复杂化的事物发生相互作用吗?
会有多少个变体?
我们是否仅需分配就可以计算密钥?
它是本地还是远程缓存?
用户之间共享吗?
站点之间共享吗?
它是依靠量子纠缠还是调试它只是让您认为呢?
缓存是什么颜色?
所有这些都是提出来并影响缓存决策的问题。我将尝试通过这篇文章覆盖它们。
堆栈溢出时的缓存层
我们在Stack Overflow上拥有自己的“ L1” /“ L2”缓存,但是为了避免与上述CPU缓存混淆,我将避免以这种方式引用它们。我们拥有几种类型的缓存。在深入探究它们使用的通用位之前,让我们首先在此处快速介绍本地和内存缓存的术语。
“全局缓存”:内存中的缓存(全局,每个Web服务器,并在未命中时由Redis支持)
通常情况下,诸如用户的最高收入限额之类的东西在网络上共享
这将命中本地内存(共享密钥空间),然后是Redis(共享密钥空间,使用Redis数据库0)
“站点缓存”:内存中缓存(每个站点,每个Web服务器,并在未命中时由Redis支持)
通常,每个站点的问题列表或用户列表之类的内容
这会命中本地内存(使用前缀的每个站点密钥空间),然后是Redis(使用Redis数据库的每个站点密钥空间)
“本地缓存”:内存缓存(每个站点,每个Web服务器,无任何支持 )
通常情况下,价格便宜但可串流播放且Redis跃点不值得
这只会影响本地内存(每个站点的键空间,使用前缀)
我们所说的“每站点”是什么意思?堆栈溢出和站点的堆栈交换网络是 一个多租户架构。堆栈溢出只是 数百个站点之一。这意味着Web服务器上的一个进程将托管所有站点,因此我们需要在需要的地方拆分缓存。而且我们必须清除它(我们还将介绍它的工作原理)。
Redis
在讨论服务器和共享缓存的工作方式之前,让我们快速介绍一下共享位的基础:Redis。那么,Redis是什么 ?这是一个开放源代码键/值数据存储,具有许多有用的数据结构,附加的发布/订阅者机制以及坚如磐石的稳定性。
为什么不使用Redis <something else>?好吧,因为它有效。而且效果很好。当我们需要共享缓存时,这似乎是一个好主意。这 真是令人难以置信的 坚如磐石。我们没有等待-它的速度非常快。我们知道它是如何工作的。我们对此非常熟悉。我们知道如何监视它。我们知道如何拼写。我们为此维护了最常用的开源库之一。如果需要,我们可以调整该库。
这是我们的基础设施 只是不用担心。我们基本上认为这是理所当然的(尽管我们仍然有HA副本设置–我们并不 完全 疯狂)。在选择基础架构时,您不只是为了获得可能的价值而进行更改。改变需要付出努力,花费时间并带来风险。如果您拥有的东西运作良好并且需要您做什么,为什么还要花时间和精力去冒险?好吧...你不知道。随着时间的流逝,您可以做数千件更好的事情。就像辩论哪个缓存服务器是最好的一样!
我们有一些Redis实例来区分应用程序的关注点(但在同一组服务器上),这是一个看起来像这样的示例:
出于好奇,上周二(2019-07-30)的一些快速统计信息涵盖了主框上的所有实例(因为我们将它们拆分用于组织工作,而不是性能……一个实例可以轻松完成我们的所有工作):
我们的Redis物理服务器具有256GB内存,但使用的内存少于96GB。
每天处理1,586,553,473条命令(由于副本,在所有实例中,每秒3,726,580,897条命令和每秒86,982个峰值)
整个服务器的平均CPU利用率为2.01%(峰值为3.04%)(即使是最活跃的实例,均<1%)
124,415,398个活动密钥(包括副本的422,818,481个)
这些数字遍及308,065,226个HTTP命中(其中64,717,337个是问题页面)
注意:这些都不是Redis的限制-我们还没有任何限制。只是我们的实例上有多少活动。
还有其他非缓存原因,我们使用Redis,即:我们还对Websocket使用pub / sub机制 , 以提供有关乐谱,代表等的实时更新。Redis5.0 添加了Streams ,这非常适合我们的Websocket,我们当其他一些基础架构就绪时(可能主要受Stack Overflow Enterprise版本的限制),它们可能会迁移到它们。
作者介绍