请参与 Prometheus 用户调研(2026 年 3 月版) ,帮助社区确定未来开发工作的优先级!

Prometheus 中的未缓存 I/O

2026年3月5日作者 Ayoub Mrini (@machine424)

你是否发现自己一直在查找 container_memory_usage_bytescontainer_memory_working_set_bytescontainer_memory_rss 之间的区别?选错了,你的内存限制就会骗你,你的基准测试会误导你,你的容器会因内存不足而被 OOMKilled。

你不是一个人。甚至有一个长达9年的Kubernetes问题 ,它捕捉了用户的沮丧情绪。

解释很简单:RAM 的使用方式并非只有一种。最容易被忽视的一点是页面缓存 的语义。对于某些容器来说,页面缓存占用的内存可能构成报告使用量的大部分,即使这部分内存大多是可回收的,从而在这些指标之间造成惊人的差异。

注意这里讨论的功能目前仅支持 Linux。

Prometheus 会向磁盘写入大量数据。毕竟,它是一个数据库。但并非所有写入都受益于页面缓存。压缩写入是最好的例子:一旦一个块被写入,只有一小部分数据可能很快再次被查询,而且由于无法预测是哪一部分,所以缓存所有数据几乎没有回报。 use-uncached-io 功能标志正是为了解决这个问题而创建的。

对于这些写入绕过缓存可以减少 Prometheus 的页面缓存占用,使其内存使用更可预测且易于理解。它还能减轻共享缓存的压力,降低驱逐查询和其他读取实际依赖的热数据的风险。一个潜在的好处是减少了缓存分配和驱逐带来的 CPU 开销。整个过程中的硬性约束是避免 CPU 或磁盘 I/O 出现任何可衡量的性能下降。

此标志在 Prometheus v3.5.0 中引入,目前仅支持 Linux。在底层,它使用直接 I/O,这需要适当的文件系统支持和 v2.4.10 或更高版本的内核,不过你大可放心,因为该版本已发布近 25 年了。

如果直接 I/O 在这里有帮助,为什么不早点实施呢?为什么不将其应用于所有有益之处呢?因为直接 I/O 具有严格的对齐要求。与缓冲 I/O 不同,你不能简单地将任何内存块写入文件的任何位置。文件偏移量、内存缓冲区地址和传输大小都必须与底层存储设备的逻辑扇区大小(通常为 512 或 4096 字节)对齐。

为了满足这些限制,实现了一个类似 bufio.Writer 的写入器 directIOWriter。在 Linux 内核 v6.1 或更高版本上,Prometheus 通过 statx  获取精确的对齐值;在旧内核上,则使用保守的默认值。

directIOWriter 目前仅覆盖压缩期间的块写入,但仅此一项就占用了 Prometheus I/O 的很大一部分。结果是显而易见的:基准测试显示页面缓存使用量减少了 20–50%,这通过 container_memory_cache 来衡量。

benchmark1

container_memory_cache 随时间变化,基准线(上) vs. use-uncached-io(下)

benchmark2

内存指标分解,基准线 vs. use-uncached-io

这项工作尚未完成,欢迎贡献。以下是一些可以帮助该功能更接近通用可用性的领域

覆盖更多写入路径

直接 I/O 目前仅限于压缩期间的块写入。索引文件和 WAL 写入是下一个自然的候选,尽管它们需要一些额外的工作。

围绕 directIOWriter 建立更多信心

所有现有的 TSDB 测试都可以使用专用的构建标签针对 directIOWriter 运行:go test --tags=forcedirectio ./tsdb/。欢迎更多涵盖写入器本身边缘情况的测试,甚至有一个想法是正式验证它从不违反对齐要求。

试验 RWF_DONTCACHE

RWF_DONTCACHE 在 Linux 内核 v6.14 中引入,它启用了未缓存的缓冲 I/O,数据仍然通过页面缓存,但相应的页面随后被丢弃。值得测试这是否能在没有直接 I/O 对齐限制的情况下提供类似的优势。

Linux 之外的支持

目前仅支持 Linux。欢迎贡献以将其扩展到其他操作系统。

有关更多详细信息,请参阅引入此功能的提案 拉取请求