文档结构  
翻译进度:已翻译     翻译赏金:0 元     ¥ 我要打赏

长文慎入 - 做得很糟糕的缓存会产生不良影响。尽量不要缓存数据;如果你真的需要,确保你的做法正确。

在计算机科学中只有两件艰难的事情:缓存失效和为事物命名。

– Phil Karlton

缓存?

为了确保我们在说同一件事情,当我说“缓存”,我指的是一种加快应用的实践,主要方式是通过记住之前的应答并使用它们对后续同样的请求进行应答,从而掩盖缓慢的依赖。

正如Phil Karlton在他著名的sound byte上简单提到的,缓存是一个很微妙的问题。它包含了我在过去几年遇到的一系列常见的错误,从而可能导致不必要的混乱延迟。

第 1 段(可获 1.64 积分)

常见错误

以下是我所看到的一些错误,以及我们应该如何应对。

在启动时缓存

当你了解到你的依赖太慢这个事实的时候,这些依赖是难以使用的,因此你在运行时甚至不会尝试他们。在你的应用启动时预先填充缓存而不是查询你依赖的服务,就是在承认你的依赖不适用于你的目标。如果这是一个第三方服务,那你可能没什么办法,但这个技术经常可以被用于避免一定要让你的服务与目标匹配。

第 2 段(可获 1.31 积分)

像这样的缓存会遇到的问题是,它延长了应用启动时间,使得扩展、错误恢复变得不可行。

即使你允许一个服务的慢启动或慢重启(你不该允许),这会生成一个与数据的特性或你的服务的使用模式几乎没有关系的缓存。一个简单的缓存过期策略在此毫无意义,因为策略本身的存在是为了避免再次访问你的依赖。

过早缓存

我说的不是在请求的生命周期里面过早缓存,我是说在开发周期中过早缓存。好几次我看到开发者写代码的时候,认为依赖太慢,然后在前面加上了缓存。

第 3 段(可获 1.59 积分)

从那时起,他们服务太慢的事实被隐藏了。没有理由去优化或者改进解决方案,因为缓存能够保证第二次请求更快;所以为什么要担心呢,对吧?

集成缓存

在SOLID里面的S是什么意思?单一职责。如果你的缓存直接集成到你的服务层,并且你没法不用缓存运行,你绝对是违反了这条原则。这不是我赞美这条原则的美德的地方。

缓存所有事情

这是指忙目地将缓存应用到所有外部调用来保证响应性,而不考虑其影响。更严重的是,这种方法会导致开发者和运营者甚至不知道缓存在发生,而假定底层的服务的可靠性很高,这不是正确的。

第 4 段(可获 1.74 积分)

重缓存

缓存所有事情,或是缓存很多事情,会导致“缓存缓存缓存的缓存的缓存(多级缓存)”。

在这个系列的一端,可能导致所有的外部缓存在其上一级缓存之前很快过期,导致浪费大量的时间和资源来操作从未用过的各层缓存。

在另一端,可能导致将所有涉及到的缓存的过期时间求和,因此10层每层过期时间为30分钟缓存将使得一个系统提供5小时前的数据。这一点 很违反直觉吧?

第 5 段(可获 1.24 积分)

无法清空的缓存

有时候缓存的实践可能会依赖像Redis这样的数据存储,它们有用于按需清空缓存的工具。

其余的实践,比如手动分配内存缓存或甚至一些主流框架提供的缓存都不会暴露任何缓存管理工具。这导致运营人员只有一个选择,即重启服务来清空内存。(更糟糕的是,查找缓存对应的文件系统的位置,然后手动清空它。)

我看到,一些发布的软件需要花费比正常来说多几个小时的时间供组员来一层一层处理缓存,将之清空或等待过期,然后处理下一层缓存。在这个过程中这个系统是离线的,因为它甚至无法保证内部一致。

第 6 段(可获 1.81 积分)

影响

上述错误的缓存数据的方式会加重影响,并带来我们之前从未想过的问题。

部署一个重量级缓存系统可能很花费时间,你需要等待缓存过期或者不得不打破你可以找到的每个缓存。甚至在我工作过的一些为因特网中很大一部分流量进行服务,并被认为是内容传输行业中的领导者的CDN系统中,清空内容和全局缓存配置可能会花费多达两个小时。理想情况不该是这样(Fastly 能在150毫秒内净化缓存),而且这会导致混乱(我们是否在提供新的数据?)。

第 7 段(可获 1.51 积分)

对此最自然的反应是想出一个解决办法,通常是破坏缓存。我们考虑一下这个办法。为你刚刚部署的特性提供一个解决方案,想想需要花费的时间、精力和认知负荷,只是为了补偿你刚刚花费在缓存上的时间、精力和认知负荷。

调试一个缓存系统同时成为了一个挑战,因为全力投入一个困难的调试过程会导致遗漏或忘记一些不相关的事情。3个小时的混乱后,你意识到你还没有测试你做出的特性改变呢。

第 8 段(可获 1.38 积分)

那我们该做什么

不要缓存!

好吧,有的时候缓存是你唯一的选择。你在使用网络,无论你喜不喜欢,缓存都在发生。但甚至在这个例子里面,也有比起单纯地 Cache-Control: max-age=xxx 更好的选择。

了解你的数据

你应该至少知道你的数据最后被修改的时间,现在你可以使用 If-Modified-Since 头部。如果数据没有被修改,就返回304-not-modified。现在你可以机智地使用客户端的缓存能力,而不牺牲可视性和控制。使用这个头部会让你立刻提供新的内容,并无限期地缓存。两者兼得。再进一步,如果你能够为你的数据设置版本(或只是生成你的响应的一个哈希值),你可以使用 etags 来与客户交互,使用恰当的逻辑,并且没有数据传输相关的延时。

第 9 段(可获 1.94 积分)

优化性能,不要隐藏糟糕的性能

投资探查工具,找出你的应用程序缓慢的原因,并修复它。减少重复的执行路径。整理糟糕的查询执行计划。正确使用索引。如果你在使用S3或blob存储你的数据,你可以使用Redis建立你自己的索引。Redis不仅仅是一个缓存。你可以机智地使用它来获取很多好处,而不涉及缓存带来的问题。

结尾

缓存是一个有用的工具,但它很容易被毫无征兆地滥用。

不要使用缓存,除非不得不使用;寻找你能找到的其他方式。在你使用缓存这个钝刀之前,优化你的应用。

如果你遇到任何缓存带来的基本问题,请告诉我,我可以把问题加到列表中。

第 10 段(可获 1.9 积分)

文章评论

访客
这翻译 太硬了~