重振缓存

显然,计算机科学中只有两个难题:缓存失效和命名(或者 如 Phil Karlton 的格言所说)。本月初,我们邀请了来自 TwitterFacebookSproutCorePalm 的 webOS微软的“Office On The Web”YahooGoogle 的代表与我们讨论第一个问题(以及其他事项),虽然我们也从中学到了一些关于第二个问题的知识。

缓存是网络上需要正确处理的重要问题,尤其是在移动设备上出现了大量网络应用程序之后。我们缓存峰会的目标是确定有助于我们推进缓存和 HTTP 请求效率的用例。例如,我们是否应该卷起袖子看看 Firefox 中的 HTTP/1.1 管道?HTTP 层还需要其他什么?而备受赞誉的 HTML5 AppCache(从 Firefox 3.5 开始实施)对开发人员来说真的有用吗?除了内容之外,我们还需要向网络应用程序公开什么,或者通过其他标题公开什么?

开发人员的反馈非常宝贵,并且越来越成为我们想要发展网络平台下一个组成部分的基础。网络开发人员是我们主要的组成部分之一;未来,我们希望他们能够帮助我们优先考虑我们应该实施什么,以及我们需要重点关注网络标准的哪些方面。我们明智地选择了与会者;如果有任何群体能够谈论大规模网络应用程序、缓存的当前性能以及他们对网络平台未来浏览器缓存行为的愿望,那就是这群人。他们给我们的反馈很多而且有用——我们的工作已经摆在我们面前。值得注意的是,我们将针对以下几个方面采取行动:

  • 增加我们的磁盘和内存缓存的默认大小。Firefox 的磁盘缓存目前设置为 50MB,考虑到目前硬件上可用的磁盘空间,这个数字有点小(尽管可以通过高级偏好设置增加这个限制,但实际上很少有用户更改默认值)。这对我们来说是容易解决的问题。一个有趣的问题是,我们是否应该启发式地确定磁盘缓存大小,而不是选择新的上限。 Steve Souders 参加了我们的缓存峰会,他在博客中谈到了 缓存大小以及过早的缓存驱逐

  • 进行一个 “Mozilla 测试飞行员项目”,以获取更多关于我们目前如何准确地缓存资源的数据。这是关于更新我们的缓存算法的一个更大的问题的一部分。与其他浏览器一样,我们使用最近最少使用 (LRU) 缓存算法的优化版本,称为 LRU-SP。我们想要收集的数据包括确定缓存资源的命中率、均值、方差和分布。平均寿命是多少?对于某些应用程序,我们的 LRU-SP 替换策略在哪些模式下效果不佳,其中大型资源(例如基本脚本文件)可能在小型资源(例如地图瓦片)之前被删除?我们还必须研究反病毒软件在定期清除缓存方面所起的作用,从而导致相关资源被过早驱逐的现象。

  • 探索根据 MIME 类型对缓存中的资源进行优先级排序。例如,允许 JavaScript (text/javascript) 在我们的 LRU-SP 算法中始终获得更高的优先级,以便确定哪些内容会被清除。一个很好的后续操作是让 Chrome、IE、Apple 和 Opera 与我们一起讨论这个问题,然后记录我们提出的基于 MIME 类型“朴素”优先级排序的内容。我们还希望允许开发人员自行设置资源优先级,也许可以通过标题来实现。这可能会成为一个持续讨论的话题。

  • 真正找出什么阻碍了 HTTP/1.1 管道 在网络上的采用,包括来自代理的数据以及它们是如何清除的。虽然管道在 移动 Firefox 版本(例如在诺基亚 N900 和 Android 设备上运行的版本)中默认启用,但在桌面 Firefox 版本中已禁用。我们这样做有各种原因,最重要的是,如果管道请求中的一个请求减慢了其他请求的速度,可能会导致性能下降。从我们的讨论中可以清楚地看出,许多参加我们缓存峰会的人认为管道将有助于他们的应用程序更好地执行。现在网络上关于管道的现状是一种人质困境:在主要桌面浏览器中,没有人启用管道,因为害怕出现减慢性能的情况(导致该特定浏览器被指责为“速度慢”)。访问我们的开发人员扔下了这块石头;至少我们必须弄清楚什么阻碍了管道在网络上的使用,并确定我们实际上可以做些什么来消除这些障碍。

  • 找出如何发展 HTML5 AppCache,坦白地说,它并没有达到我们最初预期的采用率。虽然我们倾向于将 HTML5 的部分内容(例如 缓存清单和window.applicationCache)视为另一个性能工具(以确保网络应用程序在后续访问时快速加载),但它与通用浏览器缓存不同。名称有什么意义,为什么命名是计算机科学中最难的问题之一?使用“缓存”这个词来描述处理离线网络应用程序的 HTML5 部分,让一些开发人员感到困惑。我们所说的HTML5 AppCache主要是为了实现离线网络应用程序的使用,但许多应用程序(例如使用 SproutCore 构建的应用程序)将它视为辅助缓存,以确保应用程序具有快速的启动时间并总体上执行良好。我们被问到,为什么我们应该拥有样东西:一个通用浏览器缓存,以及专门用于离线使用的其他东西?一方面,HTML5 AppCache 允许网络应用程序像原生应用程序一样运行(在某些移动手机上启用“快速启动图标”),也许最终会与原生应用程序启动器集成。另一方面,HTML5 AppCache 与通用缓存的分离可能意味着我们创建不同的 API 来处理通用缓存。总的来说,同时发展包含“缓存”的多个 API 可能令人困惑。但这就是为什么命名是难题之一,也是为什么我们必须在构建下一代架构时考虑到冗余和混淆的可能性。

  • 我们有一个 跟踪错误,标题大胆:“改进 HTTP 缓存”。你会看到我们想在这里引入的所有变化,包括将我们的缓存与 Chromium 的缓存进行基准测试(如果需要,也许可以使用 Chromium 的缓存代码)。

    缓存很重要,但也很难。可以说,网络的短期发展大多数都是这样的,无论是引入可索引数据库功能、流媒体视频还是 CSS 中的新布局模型。这些演变不一定会发生在标准机构内或在邮件列表中,而是通过快速原型制作和有意义的反馈来实现。这就是为什么我们必须与网络开发人员进行交流,以帮助我们做正确的事情,也是为什么我们将继续组织像最近的缓存峰会这样的聚会。

    关于 Arun Ranganathan

    更多 Arun Ranganathan 的文章...


23 条评论

  1. 匿名

    缓存归根结底就是正确命名。:)

    就我个人而言,我非常希望看到一种跨站点的、内容寻址的缓存机制。“从我的服务器加载 jquery-1.4.2.min.js,其哈希值为 $DECENT_HASH,但如果您已经拥有具有该哈希值的该文件,即使它不是来自我的服务器,也请使用它。”

    这将减少出于性能和缓存原因加载第三方脚本的诱惑,从而提高安全性。

    2010 年 5 月 3 日 下午 1:16

  2. Ted Mielczarek

    提醒一下,上次我们尝试启用管道时,只针对 SSL 站点(https://bugzilla.mozilla.org/show_bug.cgi?id=414477),我们仍然遇到了一个问题,即有些站点使用的是不理解管道的反向代理,最终导致我们破坏了人们的银行网站(https://bugzilla.mozilla.org/show_bug.cgi?id=422978)。据我所知,对性能的担忧与之无关,而由于中间代理的错误而“破坏网络”则与之息息相关。

    2010 年 5 月 3 日 下午 1:19

  3. Arun Ranganathan

    @匿名,我同意冗余的第三方脚本缓存应该是一个可以解决的问题,我们以前也讨论过类似的解决方案。我还经常想知道,我们是否不应该通过捆绑常见的库来简化生活。@Ted——你说“对性能的担忧”不是*唯一*原因,但我们仍然处于人质困境阶段,我认为我们需要向下游代理进行宣传。这可能是一个收集关于什么阻碍了管道使用的数据,然后像我们以前针对 DOM 更改那样敲门访问网站管理员的案例。

    2010 年 5 月 3 日 下午 2:19

  4. Alkarex

    桌面版 Opera(已在 10.53 版本上验证)默认启用了管道。
    opera:config#Performance|EnablePipelining
    http://en.wikipedia.org/wiki/HTTP_pipelining#Implementation_in_web_browsers

    2010 年 5 月 3 日 下午 3:03

  5. discoleo

    “从…加载 jquery-1.4.2.min.js”和“第三方脚本缓存”——我完全同意这些评论。一组标准化的第三方库(如 C/C++ 库)。可能应该允许对这些库进行版本控制,并且网站应该定义运行所需的最低版本。

    一些网站还使用复杂的 js 来呈现页面,所以我的问题是:缓存 js/页面的编译版本是否可行?这将提供显著的提速,因为浏览器不需要运行所有的 js 并重新渲染整个页面。

    2010 年 5 月 3 日 下午 3:47

  6. Yusuf Goolamabbas

    有很多旧的 Squid 安装,Squid 基本上与客户端使用 HTTP/1.0 协议通信。这将阻碍管道化。

    您需要与 Squid 和 Yahoo Traffic Server 等代理缓存开发人员合作,以确保它们能够与管道化正常协作,然后鼓励系统管理员升级到受支持的代理缓存。

    2010 年 5 月 3 日 下午 3:58

  7. Ehsan Akhgari

    我认为我们可以对缓存清除算法进行一项简单的改进,即使用 Places 概念中的“近期使用频率”来确定哪些条目最不常访问,并优先清除这些条目。

    2010 年 5 月 3 日 晚上 7:21

  8. sysKin

    我可以建议一个要点“检查所有缓存错误并找出容易解决的问题”。例如,每次下载较大的文件时,Firefox 缓存都会被完全清除。参见错误 531700。

    2010 年 5 月 3 日 晚上 8:57

  9. Ehsan Akhgari

    我们可以使用 Places 概念中的“近期使用频率”作为缓存条目清除的一个简单方法,即优先清除最不常访问且访问时间最久的资源。

    2010 年 5 月 3 日 晚上 10:11

  10. Alkarex

    桌面版 Opera(已在 10.53 版本上验证)默认启用了管道。
    opera:config#Performance|EnablePipelining
    (新消息,因为我昨天发的评论似乎被过滤了)

    2010 年 5 月 4 日 上午 11:50

  11. Dan

    我希望在采用 Chrome 的缓存机制之前,能看到一个好的尝试来改进缓存。我一般不是那种固执己见的人,只是我认为 Mozilla 有能力创造出同样好甚至更好的东西,如果过多地借鉴其他产品,那么竞争将会减少(参见 Apple 的 JIT 汇编器,Chrome 的 IPC 代码)。

    2010 年 5 月 4 日 下午 12:37

  12. Yoav Weiss

    我认为,阻碍管道化采用的两个主要原因是:
    * 服务器无法通知浏览器它支持管道化。规范建议采用“试错”方法,当服务器不支持管道化时会导致性能问题,而当服务器无法处理多个请求时会导致兼容性问题。
    * 无法以与请求到达顺序不同的顺序发送响应。如果对早期请求的响应由于某些原因而延迟,则可能会导致性能问题。管道中的其他响应会被阻塞,无法返回给用户。

    我们可以使用以下方法优雅地解决这两个问题:
    * 一个“X-pipeline-support: yes”响应头,用于指示它支持管道化。
    * 一个“X-pipeline-id: ”请求和响应头,使服务器能够按顺序发送响应,并防止潜在的性能损失。

    从某种程度上来说,谷歌的 SPDY 协议提议已经解决了这两个问题,但我认为有必要恢复管道化(并进行一些扩展以使其更易用),从而保持当前协议栈,以几乎类似的方式提供类似的好处,而无需“打破 Web”。

    2010 年 5 月 5 日 上午 0:22

  13. Kim Sullivan

    增加磁盘缓存的大小——不要忘记磁盘缓存对 SSD 驱动器的影响。已经有很多页面展示了如何完全禁用磁盘缓存,或者如何将其移动到内存或 RAM 盘中。

    我不知道 50MB 或 100MB 磁盘缓存对 SSD 驱动器磨损的实际影响,但即使是 50MB 的磁盘缓存也会让人感觉 Firefox 缩短了 SSD 磁盘的寿命。更大的缓存可能会使这个问题得到改善(相对减少了清除和重新加载次数,减少了重复写入次数,延长了寿命),也可能会变得更糟(通常会占用更多数据)。也许可以针对写入磁盘缓存的数据量进行一些测试,然后进行一些文档和营销工作,向用户解释这个问题?或者对 SSD 驱动器进行特殊处理?

    2010 年 5 月 5 日 上午 0:53

  14. Arun Ranganathan

    @Kim: 你能指出哪些数据表明 Firefox 通过磁盘缓存“缩短了”SSD 磁盘的寿命吗?你的意思是持续的写入/清除操作会真正缩短磁盘的寿命吗?我还不确定我们是否能对 SSD 磁盘进行优雅的例外处理,但更多的数据将会有所帮助。

    2010 年 5 月 7 日 下午 2:14

  15. Kim Sullivan

    @Arun - 正如我在帖子中所说,我不知道 Firefox 的缓存对 SSD 驱动器的实际影响,只是认为对 SSD 磁盘的写入是“邪恶”的。

    只是 SSD 驱动器确实有写入数据量的限制(你不能仅仅覆盖一些旧数据,你需要先擦除整个帧才能再次写入,而且你只能擦除有限的次数,直到电化学反应停止工作,或者其他什么原因)。例如,Kingston 估计,在 64GB 的 SSD 磁盘上,你可以每天写入 22GB 的数据,持续 5 年 (http://www.tomshardware.com/gallery/Endurance-Write-Limits,0101-237939-0-2-3-0-jpg-.html)。现在,我不认为普通用户每天会下载 22GB 的网页,因此在 SSD 驱动器上启用磁盘缓存是非常安全的,不会以任何显著的方式缩短磁盘的寿命。

    但仍然有一种观念认为,昂贵的 SSD 驱动器(截至今天,这意味着所有 SSD 驱动器)不应该用于诸如磁盘缓存之类的用途——基本上,它们应该只作为主要用于读取的启动驱动器,而所有常规数据应该移动到其他位置(例如磁盘缓存、配置文件,甚至 Windows 页面文件)。

    因此,互联网上有很多页面教你怎么禁用缓存,只需谷歌搜索“ssd disable firefox cache”(例如 http://www.overclock.net/hard-drives-storage/475114-howto-get-most-out-your-ssd.html ——“由于 SSD 的写入周期有限,Firefox 的将缓存文件写入磁盘的行为并不好)。

    我并没有说这是好的建议还是不好的建议——我只是不知道。我不知道 Firefox 每天将多少数据写入其文件缓存,也不知道它每天将多少数据写入 SQLite 数据库,以及每天使用 Firefox 会如何缩短 SSD 磁盘的寿命(尤其是因为驱动器供应商对实际寿命似乎非常含糊其辞)。

    因此,我认为进行一项 TestPilot 研究,收集有关典型用户每天写入硬盘的数据量的信息,然后向用户解释在 SSD 磁盘上启用磁盘缓存的影响,是一个好主意。

    也许更大的磁盘缓存会使情况变得更好,因为文件不必经常重新下载。

    2010 年 5 月 7 日 下午 2:53

  16. Baboo

    据我所知,只要 8192 个对象错误没有解决,增加缓存大小就毫无意义……

    2010 年 5 月 9 日 上午 10:00

  17. Baboo

    @Arun:http://en.wikipedia.org/wiki/Solid-state_drive#Disadvantages

    2010 年 5 月 9 日 上午 10:28

  18. Brett Zamir

    是否有合适的论坛可以贡献有关该主题的意见(高层次的)?我有一些想法可能超出了你的范围,但我认为它们仍然可以很好地融入缓存讨论中。

    除其他想法外,我认为允许使用一个特殊的协议链接,并可选地指向带有清单的文件包(或指向包的清单),这不仅可以触发资源的解压缩和缓存,还可以指定更新条件(对于清单本身及其组成部分),以及可选地将包注册为协议/URN 或内容处理程序(或者如果可以创建这样的东西,则注册为命名空间处理程序),这样共享缓存就可以变得可行且健壮。

    它不仅可以缓存像 jQuery 这样的 Web 上经常使用的文件,还可以允许像学术界常用的 TEI XML 语言(现在正在申请自己的内容类型)这样的语言,指向默认的 XSL 样式表,这些样式表可以用于 Web 上遇到的任何 TEI 文件,从而避免了重新下载大量样式表的需要,从而使这种语义非常丰富的 XML 文档在 Web 上变得实用(并且不会通过将其转换为 XHTML 来使事情复杂化或使其变得简单)。

    允许使用单一的标准分层(最好是 XML)文件打包清单格式(鉴于 Web 主要限于指定单个页面以及一些配套资源)还有其他优点,例如通过单个 HTTP 请求访问捆绑的资源,分层站点地图,XLink 样式的链接库,供浏览器或搜索引擎用来建议从/到网站的链接,Gopher 2.0 从链接集到链接集的导航,以及我最喜欢的,透明的客户端混搭(以及混搭、混搭和混搭:参见 http://brett-zamir.me/webmets/index.php?title=Main_Page ),这些混搭可以通过 JavaScript、XSL 或 XQuery 来组装。

    美国国会图书馆有一个 XML 标准,METS(参见 http://www.loc.gov/standards/mets/ ),它可以作为这种清单的良好开端,但需要进一步标准化,才能在不同的浏览器之间工作并满足 Web 的需求。

    我记得,HTML5 编辑建议使用 IETF 提议来适合这种提议,但我恐怕目前我还没有能力自己编写这样的规范。是否有人有兴趣合作或将我引导到合适的场所来提出这个问题?也许是 Drumbeat?

    2010 年 5 月 12 日 晚上 8:01

  19. Jimmy

    @Yoav Weiss 通过宣传 HTTP 服务器支持 HTTP/1.1,服务器就声明了它支持管道化,因为这是 HTTP/1.1 标准的一部分,不是吗?

    2010 年 5 月 17 日 下午 3:37

    1. Christopher Blizzard

      不幸的是,由于存在大量中间代理,而这些代理无法正确处理管道化,因此仅仅宣传 1.1 支持是不够的。

      2010 年 5 月 17 日 晚上 8:12

  20. Yoav Weiss

    @Christopher Blizzard - 你是对的,我之前提议的内容没有涵盖代理。因此,我想建议一些改进措施。
    我们需要一种机制来帮助检测中间代理,并使它们能够声明它们对管道化的支持。如果存在这样的代理,并且它没有声明对管道化的支持,那么它很可能是一个旧的代理,我们应该避免使用管道化。

    代理检测可以通过以下方法实现:
    * “X-pipeline-hash”请求头字段,它将对所有请求头及其值(除了 X-pipeline-hash 头本身)进行哈希。
    * 一个等效的“X-pipeline-hash”响应头字段,它将对响应头和值进行哈希运算

    这将使浏览器和服务器都能知道请求或响应是否在传输过程中被修改。如果是,则存在中间代理。

    代理声明管道支持的机制可以是
    * 浏览器默认情况下会发送“X-pipeline-proxy: no”请求头字段
    * 支持管道的代理会将该头字段修改为“X-pipeline-proxy: yes”
    * 服务器会将接收到的头字段镜像到响应中

    这样,如果未检测到代理,或者检测到支持管道的代理,则客户端可以在收到初始响应后开始发送管道请求。

    可能存在此机制失效的情况,但可以使用当前的“在发生故障时保存已发送请求”的回退机制。

    显然,这个建议是我刚刚想到的,还有改进的空间,但 IMO 它可以提供一个足够好的方法来开始推动管道并使网络变得更好一点。

    我希望我表达清楚了……

    Yoav

    2010年5月18日 下午 02:51

  21. […] 原作者:Arun Ranganathan – 返回原文 […]

    2010年6月5日 下午 02:58

  22. 各种流行的网络浏览器使用哪些缓存替换策略?…

    根据 [1],大多数当前浏览器使用一种称为 LRU-FP 的缓存替换策略,该策略由 Cheng 和 Kambayashi 在 2000 年提出 [2]。在 Web 缓存的背景下,缓存替换比在传统系统(即 CPU 缓存层次…)中要困难得多。

    2012年6月16日 下午 11:44

本文的评论已关闭。