🔗 Feature: Rock Store
- 目标:磁盘缓存性能达到现代硬件极限的80%。
- 状态:已完成。
- 版本: 3.2
- 开发者:AlexRousskov
- 更多:非官方 v3.1 实现 和未来的大响应 支持。
🔗 范围
大型、繁忙的网站需要一种磁盘存储方案,该方案能够接近现代系统(拥有 8+GB RAM、4+ CPU 核心和 4+ 个专用、75+GB、10K+ RPM 磁盘)的硬件极限。Rock Store 旨在利用从 COSS(写寻道优化)和 diskd(SMP 可扩展性)中学到的经验,同时测试和实现新的优化技术,来提供这种存储。
🔗 当前状态
- SMP Squid:Rock Store 自 Squid 版本 3.2.0.13 起可用。它已经过一些实验室和有限部署的测试。它需要在各种环境中表现良好还需要更多工作,但似乎在某些环境中是可用的。除非另有说明,本页面讨论的是 Rock Store 的官方 SMP 实现。
🔗 架构
当前设计包含以下主要组件
- 共享内存缓存:Squid 工作进程用于存放“热”缓存对象的共享内存存储。当使用共享内存缓存时,本地工作进程缓存会尽可能禁用。
- 共享 I/O 页:Squid 工作进程和 Rock “disker”使用的共享内存存储,用于交换正在进行 swap in 或 swap out 的对象数据。
- Rock diskers:每个 cache_dir 一个进程,负责低级阻塞 I/O。一个 disker(或一个 Rock cache_dir)旨在用于每个专用于 Squid 磁盘缓存的物理磁盘。概念上,Rock diskers 类似于 diskd 进程。
- Squid workers:常规的 Squid SMP 工作进程。驻留在工作进程中的 Rock Store 代码与 diskers 进行通信。
- 无锁共享内存队列:连接工作进程和 diskers 的原子非阻塞队列。队列将 I/O 请求从工作进程传递给 diskers,并将“就绪/失败”确认从 diskers 传递给工作进程。实际的对象数据不通过队列传递,而是通过共享 I/O 页进行交换。
如果未使用 Rock diskers,Squid 工作进程可以共享内存缓存。
未来的实现可能会最终消除共享 I/O 页和共享内存缓存之间的复制,但这将需要更改 Squid 中的低级内存缓存结构,这将非常困难。
🔗 缓存日志消息
本节详细介绍了与 Rock 存储相关的部分 cache.log 消息。
🔗 Worker I/O push queue overflow: ipcIo1.5225w4
一个工作进程试图存储 MISS 或加载 HIT,但由于 disker 的 I/O 请求队列已满而失败。用户可见的影响取决于问题发生的时间和方向
- 存储第一个 MISS slot 时出错:对最终用户没有明显影响,但响应将不会被缓存。
- 存储后续 MISS slot 时出错(仅限 Large Rock):对最终用户没有明显影响,但响应将不会被缓存。
- 加载第一个 HIT slot 时出错:磁盘 HIT 将被转换为缓存 MISS。
- 加载后续 HIT slot 时出错(仅限 Large Rock):响应被截断。
如果这些警告持续存在,那么您将使磁盘过载,降低命中率,并增加命中罕见错误恢复代码中 bug 的可能性。您应该调整您的 Squid 以避免这些警告。
除了过载之外,另一个可能导致这些警告的原因是 disker 进程死亡。目前尚不清楚 Squid 是否能够很好地从这种情况中恢复,即使 Squid 主进程重启了 disker。
🔗 WARNING: abandoning N I/Os after at least 7.00s timeout
一个工作进程发现其 N 个 I/O 请求在 disker 队列中等待超时(无响应)。用户可见的影响取决于问题发生的时间和方向。所有“Worker I/O push queue overflow”警告的影响都适用于此处,此外还有以下注意事项:
- 存储第一个 MISS slot 时超时:内存使用量增加,甚至延迟(暂时停滞)的 MISS 事务?
- 存储后续 MISS slot 时超时(仅限 Large Rock):与第一个 MISS slot 相同。
- 加载第一个 HIT slot 时出错:延迟转换为 MISS。
- 加载后续 HIT slot 时出错(仅限 Large Rock):响应截断前的延迟。
除了过载之外,另一个可能导致这些警告的原因是 disker 进程死亡。目前尚不清楚 Squid 是否能够很好地从这种情况中恢复,即使 Squid 主进程重启了 disker。
🔗 WARNING: communication with disker may be too slow or disrupted for about 7.00s; rescued N …
一个工作进程意外发现其 N 个 I/O 请求已准备好进行处理,但正在等待 disker 发出的“检查结果队列!”通知,而该通知从未到来。被救援的 I/O 请求现在将正常进行,但它们已暂停一段时间,这可能会影响最终用户,尤其是在 HIT 时。此外,如果由于持续过载条件或 disker 死亡而丢失了 disker 通知,这种临时恢复从长远来看将无济于事。
🔗 性能调优
Rock Store 是为高性能磁盘缓存环境而开发的。这影响了作者所做的各种权衡。
- 如果您希望您的 Squid 在使用磁盘缓存的情况下处理高负载,则应考虑使用 Rock Store。但是,Rock Store 在高负载下如果没有进行调优,则表现不佳。本节包含一些调优建议,旨在帮助您调优 Rock 缓存,但调优过程很复杂,需要对文件系统行为有一定的理解(更不用说实验了)。
- 如果您不太关心性能,并且不需要 SMP 磁盘缓存,那么您可能想改用 ufs 缓存存储。使用 ufs 缓存可以避免您不需要的性能调优相关的开销。
- 如果您不太关心性能,但需要 SMP 磁盘缓存,目前没有好的选择。这种情况应该很少见,因为 SMP 通常意味着性能很重要。
Rock diskers 的工作速度尽可能快。如果它们比 Squid 工作进程产生的 swap 负载慢,那么磁盘队列将增长,导致溢出和超时警告。
2011/11/15 09:39:36 kid1| Worker I/O push queue overflow: ipcIo1.5225w4
2011/11/15 09:39:42 kid1| WARNING: abandoning 217 I/Os after at least 7.00s timeout
2011/11/15 09:39:42 kid2| WARNING: communication with disker may be too slow or disrupted for about 7.00s; rescued ...
当您的操作系统文件系统缓存大量磁盘写请求到 RAM,然后进入写狂热状态时,很可能会出现类似的问题,通常会阻止所有执行 I/O 的进程,包括 Squid diskers。通过仔细调整文件系统参数以防止写入请求过度聚合,可以避免或最小化这些问题。通常,仅调整文件系统不足以解决问题,您的磁盘仍然会落后于工作进程。
当您的磁盘无法跟上提供的负载时,您应该在 Rock cache_dir 行中添加 max-swap-rate 和 swap-timeout 选项。在大多数情况下,您需要这两个选项,或者都不需要。第一个选项告诉 Squid 调整 Rock cache_dir 的流量(在必要时人为延迟 I/O 以防止交通堵塞),第二个选项告诉 Squid 何时应避免磁盘 I/O,因为这需要“太长时间”。
最佳值取决于您的负载、硬件和命中延迟容差,因此无法为所有情况提供单一公式,但有一个算法您可以作为起点。
- 设置 max-swap-rate 以限制每秒平均 I/O 次数。如果您使用专用的普通现代磁盘,请使用 200/秒。如果您的磁盘非常快且寻道时间优于平均水平,请使用 300/秒。如果您的磁盘不太好,请使用 100/秒。
- 设置 swap-timeout 以限制 I/O 等待时间。超时值越低,您获得的磁盘命中次数就越少,因为存储和加载的对象将越少。另一方面,过高的值可能会导致命中比未命中更慢。请记住,配置的超时值是与预期的 swap 等待时间(包括排队延迟)进行比较的。它 *不是* 您的磁盘执行单个 I/O 所需的时间(如果一切都平衡,平均单个 I/O 将需要 *1/max-swap-rate* 秒)。如果您不知道从哪里开始,请从 300 毫秒开始。
- 尝试配置的值,但 *不要被* 最初的出色性能所迷惑。在许多环境中,您将获得出色的结果,直到操作系统开始将缓存的 I/O 请求写入磁盘。使用 iostat 或类似的性能监控工具来观察写入 *是否* 已写入磁盘。使用 iostat 或类似的工具来测量磁盘负载,通常报告为“利用率”百分比。归档您的测量结果。
- 如果您的测量磁盘利用率经常超过 90%,则降低 max-swap-rate。如果命中感觉太慢,则降低 swap-timeout。如果 Squid 警告队列溢出,则降低其中一个或两者都降低。您可以使用 extreme 值,例如 max-swap-rate=1/sec,来检查问题是否可以通过此方法解决。每次更改后重复测试。
- 如果您的测量磁盘利用率从未高于 80%,则增加 max-swap-rate。如果您可以容忍较慢的命中,则增加 swap-timeout。您可以完全删除限制以检查它们是否真的有必要。每次更改后重复测试。
一如既往,一次只更改一件事通常是个坏主意:耐心是一种美德。不幸的是,大多数 Rock cache_dir 参数在不停止 Squid 的情况下是无法重新配置的,这使得一次更改变得痛苦,尤其是在实时部署环境中。请考虑首先在现实的实验室环境中进行基准测试和调优 Squid。
理想情况下,您应该构建一个数学模型,解释为什么您的磁盘性能是这样的,考虑到您的磁盘参数、cache_dir 设置和提供的负载。准确的模型消除了盲目实验的需要。
上述过程在某些情况下有效,但并非全部。您的体验可能会有所不同。
🔗 限制
- 当 cache_dirs 在工作进程之间共享时,大小超过 32,000 字节的对象无法被缓存。Rock Store 本身支持任意槽大小,但 disker 进程使用 IPC I/O(而不是阻塞 I/O),这依赖于共享内存页,而共享内存页目前被硬编码为 32KB。您可以通过修改
Ipc::Mem::PageSize()手动将共享页面大小提高到 64KB 甚至更高,但这样做会浪费更多 RAM。为了有效地支持大对象的共享缓存,我们需要教会 Rock Store 以小于槽大小的块来读写槽。 - 缓存巨大的对象速度慢,并且浪费磁盘空间和 RAM。由于 Rock Store 使用固定大小的槽,较大的槽大小会导致更多空间浪费。由于 Rock Store 使用槽大小的 I/O,较大的槽大小会延迟 I/O 完成。我们需要添加支持,通过一系列 Rock 槽来存储大对象,和/或为 UFS cache_dirs 添加共享缓存支持。
- 您必须使用轮询(round-robin)的 cache_dir 选择。我们最终会添加基于负载的选择支持。
- 大多数 cache_dir 参数在不停止 Squid 的情况下无法重新配置。这使得性能调优变得困难,尤其是在您使用实时用户作为试验品的情况下。
- 如果支持 IPC I/O 并且使用了多个工作进程,则无法强制使用阻塞 I/O。幸运的是,在大多数情况下,这并非必要,因为您希望在工作进程之间共享 cache_dirs,这需要 IPC I/O。
- 将 cache_dir 限制为特定工作进程很困难。幸运的是,在大多数情况下,这并非必要。
🔗 附录:设计选择
该项目必须回答几个关键的设计问题。下表提供了问题和我们的决定。这些信息作为历史参考保留,可能已过时。
-
- 我们是否限制缓存对象的最大大小,就像 COSS 所做的那样?
- 限制是一个管理上的痛点,并迫使许多站点配置
- 多个磁盘存储。我们希望使用专用磁盘,并且不希望
- 一个“二级”无限存储来干扰我们的 I/O。
- 然而,较小的尺寸限制简化了数据放置方案。
- 将大文件支持集成
- 到一个存储中而不使数据放置复杂化将是很好的
- 是的,我们最初限制了对象大小,因为这很简单,而且当前的代码赞助者不需要缓存大文件。后来的实现可能会目录化和链接单独的存储块以支持任意长度的文件。
-
- 我们是否要保证 100% 的存储能力和 100% 的检索能力?
- 如果我们能够跳过一些新对象或
- 覆盖旧对象,只要内存缓存处理热点,我们可能会进行更多优化。
- SMP 实现假设存储不可靠(例如,disker 可能会死亡或被阻塞),但并未利用这一点。未来的优化可能会跳过或重新排序 I/O 请求。
-
- 我们是否需要 100% 的磁盘空间利用率?如果我们
- 允许留下空隙,我们可以进行更多优化。这些空隙可能有多大
- 相对于总磁盘大小?随着磁盘存储价格的下降,
- 如果我们
- 可以获得“大量”性能,浪费“一点”存储是合适的。
- 当前实现不通过故意在磁盘上创建空隙来优化。
-
- 我们是否依赖操作系统缓冲区?操作系统级别的磁盘 I/O 优化通常
- 在高代理负载下会出错。绕过操作系统缓冲区并执行
- 原始磁盘 I/O 是否能帮助我们接近硬件极限?
- 当前实现出于简单性考虑使用了操作系统缓冲区。未来的优化可能会使用原始的、未缓冲的磁盘 I/O。
-
- 我们需要一个完整、可靠的内存缓存索引吗?我们是否应该使
- 索引更小,甚至可能不可靠,以腾出 RAM 用于内存
- 缓存?我们可以使用哈希来查找磁盘上的对象位置,而无需
- 索引吗?
- SMP 实现为共享内存缓存维护一个索引,并为每个配置的 Rock Store cache_dir 维护一个索引。这些索引在工作进程之间共享。
-
- Rock Store 的哪些部分应该是可替换/可配置的?例如,
- 设计为可以有效地支持固态硬盘
- 使用相同的存储架构是否值得?
- 当前配置仅限于块大小和并发 I/O 限制,但将来肯定会变得更复杂。我们没有机会尝试固态硬盘,但整体设计应该能够很好地适应它们。未来的代码可能会有一个选项来优化寻道延迟或相同位置写入的数量。
-
- 每个 cache_dir 的限制是否仍为 1700 万个对象?我们可以优化
- 知道繁忙的缓存将达到该限制吗?
- 当前代码在某些数据结构中继续依赖 1700 万的限制。
类别:功能
导航:站点搜索,站点页面,类别,🔼 向上