ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Neon Pageserver 直接 I/O(Direct IO)改造深度解析:让存储数据路径彻底绕开内核页缓存

Neon Pageserver 直接 I/O(Direct IO)改造深度解析:让存储数据路径彻底绕开内核页缓存 Neon Pageserver 直接 I/ODirect IO改造深度解析让存储数据路径彻底绕开内核页缓存【免费下载链接】neonNeon: Serverless Postgres. We separated storage and compute to offer autoscaling, code-like database branching, and scale to zero.项目地址: https://gitcode.com/GitHub_Trending/ne/neon本文基于 docs/rfcs/2025-04-30-direct-io-for-pageserver.md 撰写该文档是 Neon 项目对 Pageserver 直接 I/O 改造的回顾性 RFC。文章在完整继承原文档内容的基础上结合仓库内 virtual_file 模块 的源码与测试从术语背景、设计动机、实现细节到配置与验证逐层展开帮助读者理解 Neon Pageserver 数据路径WAL 摄取、compaction、Timeline::get如何从内核页缓存兜底演进为全量 Direct IO 直通磁盘的架构变迁以及virtual_file_io_mode、page_service_pipelining、get_vectored_concurrent_io等关键配置项背后的工作原理。一、导读这篇回顾性 RFC 回答了一个核心工程问题为什么 Neon Pageserver 要把数据路径上的所有文件 I/O 从经过内核页缓存的 buffered IO切换到完全绕过内核页缓存的 direct IO以及它是如何做到的。文章先厘清 direct IO、buffered IO、内核页缓存、writeback 等底层概念再回顾 Pageserver 自有的 PageCache 与 VirtualFile 抽象的历史演进随后展开高层设计动机可预测延迟、资源显式化、CPU 效率与实现细节512 字节对齐硬编码、IoBufAligned标记 trait、BufferedWriter双缓冲、virtual_file_io_mode三态开关最后给出正确性与性能验证方法及未来工作清单。读完本文你不仅能理解 Neon 这一存储架构决策的来龙去脉还能直接定位到 pageserver/src/virtual_file.rs 等源码文件按图索骥地阅读真实实现。二、背景从内核页缓存到 Direct IO 的关键术语要理解这次改造必须先厘清一组容易混淆的底层概念。RFC 原文用一段很精确的语言定义了它们这里逐条展开。2.1 内核页缓存kernel page cache与 Buffered IO内核页缓存是 Linux 内核为文件系统内容提供的 write-back 缓存。它缓存的单位是内存页大小且对齐的文件分块典型为 4KiB缓存位于内核内存中用户态无法直接访问。Buffered IO指应用的 read/write 系统调用经由内核页缓存完成。RFC 给出了一个非常直观的例子对文件偏移 5000 处做一次 10 字节的读或写内核会先把文件[4096, 8192)区间的内容加载到一个空闲页缓存页中必要时驱逐一个旧页腾出空间再在缓存页内部偏移 4 处做 10 字节的内存到内存拷贝。如果是写操作内核还会在辅助结构中记录该页已脏。2.2 Writeback 与内存压力Memory PressureWritebackbuffered 的 read/write 系统调用在内存拷贝完成后即可返回——这些修改甚至都没有真正下发到磁盘更谈不上持久化。内核会基于各种条件异步回写脏页。对 Neon 最相关的两个条件是a) 用户态显式请求fsyncb) 内存压力。内存压力内核页缓存本质是尽力而为的备用内存消费者。当没有空闲内存时内核页分配器会回收页缓存页以满足分配请求而回收脏页前必须先 writeback。这带来一个深远的后果任何匿名内存分配都可能触发 I/O——只要获取内存的唯一途径是驱逐并复用脏页缓存页。尤其值得注意的是用户态一个简单的malloc也包含在内因为它最终会落到mmap(..., MAP_ANON, ...)上。RFC 将这种效应称为 buffered IO 引起的malloc 延迟反向散射malloc latency backscatter内存分配这一本应与磁盘无关的操作却可能因为回写脏页而背上磁盘延迟。2.3 Direct IODirect IO允许应用的 read/write 系统调用绕过内核页缓存。文件系统仍然参与因为它最终负责把文件及文件内偏移映射到块设备的扇区。通常文件系统会对内存缓冲区和文件偏移提出大小与对齐要求statx的Dio_mem_align/Dio_offset_align字段不满足对齐要求时I/O 操作会在运行时以EINVAL失败。2.4 buffered vs direct本质区别RFC 指出二者的核心区别在于由谁分配并填充 I/O 缓冲区、由谁控制 I/O 下发的确切时机在 buffered IO 中是系统调用处理程序、内核页缓存和内存管理子系统参见 writeback在做这一切在 direct IO 中全部由应用自己完成。用 direct IO 编程需要应用付出更多努力回报是对内存消耗/修改与磁盘 I/O 之间有精确的控制权以及清晰的分界线。这一句话实际上就是整篇 RFC 的哲学基础。2.5 Pageserver 的专属概念PS PageCache 与 VirtualFilePageserver PageCachePS PageCachePageserver 在应用层额外维护了一层 PageCache区别于内核页缓存。它的缓存单位是 Pageserver 写入的 layer 文件的 8KiB 块PageCache 未命中时通过VirtualFile抽象层从文件系统读取填充。默认大小极小64MiB与 Postgres 的shared_buffers很像。Neon 生产环境长期使用 128MiB过去约一年里逐步上调到了2GiB。VirtualFilePageserver 的文件 I/O 抽象与 Postgres 中同名设施非常相似。它最初的历史用途是绕开打开文件描述符数量的限制在 Linux 上实际已无关紧要但在 Neon 中它作为中间层很有价值一方面承载指标统计另一方面抽象了 Pageserver 支持的多种 I/O 引擎std-fs与tokio-epoll-uring。其实现位于 pageserver/src/virtual_file.rs内部是一个全局OPEN_FILES槽位数组配合 clock 算法做文件描述符的 LRU 式回收这一点与 PostgreSQL 的虚拟文件描述符设施src/backend/storage/file/fd.c同源。模块内的单元测试test_virtual_files与test_vfile_concurrencypageserver/src/virtual_file.rs分别验证了 FD 缓存被大量打开文件打满后的自动重开以及 100 个线程并发读写同一 VirtualFile 的正确性。三、Pageserver 缓存历史PageCache 如何一步步退出数据路径RFC 花了专门一节回顾 Pageserver 缓存演进史这是理解为什么现在才做 Direct IO的关键上下文。多年以来Pageserver 的PageCache处于所有读与写 I/O 的通路上并通过 buffered IO 向内核回写。在 PR 4994 中PageCache 被改造为只读的不可变数据缓存。引入tokio-epoll-uring要求代码库改用owned IO buffersPageCache 页恰好可以作为 owned IO buffers 使用。随后Pageserver 开始让**用户数据块data blocks**绕过 PageCache。所谓 data blocks是 layer 文件中承载多个Value的 8KiB 数据块区别于告诉我们文件里有哪些值、在什么偏移的磁盘 B-tree 索引块indirect blocks。delta 层与 image 层内嵌的磁盘 B-tree 仍然留在 PageCache 中。VectoredTimeline::get参见 RFC 030 vectored-timeline-get直接跳过了 delta/image 层数据块的 PageCache 缓存其余数据块使用者Materialized page cache、InMemoryLayer、Compaction由专门的 Epic 跟进处理。结果所有数据块一律通过VirtualFileAPI 读取走内核 buffered read 路径即内核页缓存索引块磁盘 B-tree 块仍缓存在 PS PageCache 中。在生产环境中Neon 将 PS PageCache 配到2GiB使命中率提升到约99.95%在最高负载的机器上1 分钟平均的替换率replacement rate降到每秒 200 次以下。高基线替换率被视为资源耗尽的信号页缓存不足以容纳 Pageserver 工作集应对措施是迁走租户或增大 PS PageCache目前是手动操作未来可以自动化例如由 Storage Controller 驱动。RFC 还展望了远期方向也许可以完全消除间接块上的 PageCache改用以整个磁盘 B-tree 内容为单元的 LRU 缓存。四、高层设计为什么要让内核页缓存彻底出局在改造开始前Pageserver 的所有数据块读取和整个写路径都在使用内核 buffered IO。改造目标是把内核页缓存从所有与文件系统的交互中请出去。这带来三类系统性质4.1 可预测的 VirtualFile 延迟使用 buffered IO 时读有时快有时慢取决于内核页缓存命中与否使用 buffered IO 时摄取或 compaction 期间写出新 layer 文件时的追加写也会因 writeback 背压而时快时慢malloc 反向散射虽然目前不是被主动观测到的现象但 Dirty 内存量和 Memory PSI 图上偶尔出现的尖峰说明它可能已经在起作用切换为 direct IO 后上述操作将始终呈现可预测的设备延迟——读与追加写总是直达磁盘malloc也不再需要回写脏数据。4.2 资源使用的显式化与可感知性在多租户系统中对每个租户使用的主要资源保持显式通常是可取且有价值的使用 direct IO 后磁盘 IOPs与内存容量这两类资源变得显式化不再通过内核页缓存被混为一谈、游离于我们的控制之外这使得构建按租户的资源使用可观测性成为可能哪个租户真正触发了发往磁盘的 I/O也使得实现租户感知的 I/O 调度器、进而做资源记账与 QoS 成为可能——内核并不感知租户做不到这一点。4.3 CPU 效率内核页缓存的参与意味着读和写路径上各多一次内存到内存的拷贝只要让用户态 I/O 缓冲区满足 direct IO 的对齐要求direct IO 就能消除这次拷贝。4.4 明确的权衡与取舍当然代价是放弃内核页缓存的理论收益重复读取同一数据局部性引用带来的读延迟改善——前提是下次访问时该状态仍驻留于缓存内核把小的 VFS 写批量合并成更大磁盘写带来的写吞吐——前提是内存压力足够低、内核能负担延迟 writeback。RFC 明确表示乐于接受这一权衡理由有三上述优势可预测延迟、显式化、CPU 效率生产环境 Pageserver 的 DRAM 足够用 PS PageCache 承载元数据 索引块仅 2GiB 的 PS PageCache 就有平均 99.95% 命中率。因此去磁盘的延迟只发生在数据块读取上不涉及索引遍历在高校租户密度下内核页缓存本就低效详见文末附录而密集打包租户以降低 COGS 永远是值得追求的方向系统应当为此而设计。因此Neon 接受部分曾经因巧合而很快的读现在具有更高但可预测的延迟这一结果。五、期望的最终状态Desired End State项目目标在带星号的条件下已达成Pageserver 数据路径上的所有 I/O 都使用 direct IO从而绕过内核页缓存。其中数据路径具体包括WAL 摄取路径wal ingest pathcompaction一切处于Timeline::get/Timeline::get_vectored路径上的操作。配套的量化目标生产 Pageserver 配置调优到几乎所有非数据块都缓存在 PS PageCache命中率目标 99.95%摄取延迟无回退随机 getpage 请求延迟中等待磁盘时间的总贡献为O(1 次读 IOP 延迟)——通过接近 100% 的 PS PageCache 命中率实现索引遍历几乎从不等待 I/O从而可以在遍历索引的过程中并发下发所有数据块读只在最后统一等待concurrent IO对一串顺序 getpage 请求direct IO 方案摊还的等待磁盘时间贡献为每个请求1/32 × 读 IOP 延迟——通过在服务端把最多 32 个读批量合并进单次Timeline::get_vectored调用实现。这是批次满载的理想情形现实中由于队列深度不足生产环境尚未达到此状态。六、设计与实现对齐约束、并发读与双缓冲写6.1 前置条件服务端批处理与并发 IO要满足上述等待磁盘时间指标读路径上需要两件事page_service 层服务端批处理配置项为page_service_pipelining并发 IO配置项为get_vectored_concurrent_io。这两项工作由同一 Epic 跟踪推进。RFC 特别说明服务端批处理未来很可能被 compute-communicator 项目取代并发 IO 的完整设计见同期的回顾性 RFC 2025-04-30-pageserver-concurrent-io-on-read-path.md且其实现相对脆弱需要进一步投入详见该 RFC 的 Future Work 一节。在源码层面这两个配置项都定义在 pageserver/src/config.rs 中字段page_service_pipelining与get_vectored_concurrent_io并在 pageserver/src/page_service.rs 中被消费批处理配置在启动时写入指标pageserver/src/metrics.rs并发度由IoConcurrency::spawn_from_conf从get_vectored_concurrent_io构造。Timeline::get_vectored的并发读路径可在 pageserver/src/pgdatadir_mapping.rs 中看到对get_vectored_concurrent_io的多处引用。6.2 写路径前置条件BufferedWriter 双缓冲对于写路径尤其是 WAL 摄取需要隐藏写延迟。做法是实现一个BufferedWriter类型采用双缓冲已写满缓冲区的 flush 在一个 sidecar tokio 任务中完成与此同时新写入填充新的缓冲区。InMemoryLayer 以及 BlobWriter即 delta 层和 image 层的写入器都被重构为使用这个BufferedWriter。在源码中BufferedWriter位于 pageserver/src/virtual_file/owned_buffers_io/write.rs它包装一个OwnedAsyncWritertrait见同文件第 34 行用内部Buffer把小写入批量合并为Buffer::cap大小的较大写入只有当缓冲区填满pending cap时才 flush从而保证对文件系统的写入总是发生在Buffer::cap的整数倍偏移、且长度也是Buffer::cap的整数倍——这正满足 direct IO 对指针对齐、count长度倍数的要求见该文件第 48-62 行注释首个 flush 发生在构造参数start_offset之后依次是start_offset cap、start_offset 2*cap……第 67-68 行针对收尾时缓冲区未满的三种处理模式由BufferedWriterShutdownMode表达DropTail丢弃尾部数据、ZeroPadToNextMultiple补零到下一个倍数、PadThenTruncate补零 flush 后ftruncate回精确长度第 108-118 行代码注释还预留了大写入绕过内部缓冲直接下发的优化空间对应 issue #10101。此外本次改造引入了TempVirtualFilepageserver/src/virtual_file/temporary.rs并用于 InMemoryLayer 的落地文件见 pageserver/src/tenant/ephemeral_file.rs其中TempVirtualFile由EphemeralFile与BufferedWriter共同持有。6.3 如何保证满足对齐要求Direct IO 对三样东西有要求内存缓冲区对齐memory buffer alignmentI/O 大小 内存缓冲区大小io size文件偏移对齐file offset alignment。这些要求取决于文件系统 / 块设备 / 架构硬件页大小的组合。Neon 生产环境目前使用 ext4 Linux 6.1.X跑在 AWS 和 Azure 的存储优化实例本地挂载 NVMe上。Neon 没有用statx做动态发现而是静态硬编码 512 字节作为缓冲区/偏移对齐和大小倍数。决策理由a) 与所有需要运行的环境兼容b) 主要工作负载可能以小随机读为主相邻读会尽量合并但最坏情况是所有需要读取的Value相距很远c) 512 字节在生产实例类型上的尾部延迟远好于 4kp99.9 低 3 倍p99.99 低 5 倍d) 编译期硬编码允许用 Rust 类型系统强制只使用对齐 I/O 缓冲区消除 direct IO 常见的运行时错误来源。新引入的IoBufAligned/IoBufAlignedMut标记 trait表明某个缓冲区满足内存对齐要求。所有VirtualFileAPI 以及其上构建的多个软件层只接受实现了这些 trait 的缓冲区。实现者包括IoBuffer/IoBufferMut用于绝大多数读和写PageWriteGuardBuf用于填充 PS PageCache 页即索引块。在源码中可以看到这套类型体系的落点pageserver/src/virtual_file.rs 第 1255-1258 行定义了基于AlignedBufferMutConstAlign{ get_io_buffer_alignment() }的IoBufferMut、IoBuffer、IoPageSlice而对齐常量来自DEFAULT_IO_BUFFER_ALIGNMENTpageserver_api::config::defaults。RFC 特别强调对齐要求是传染性的自底向上渗透整个代码库。Neon 在owned-buffers 风格 API面向 tokio-epoll-uring停止渗透的同一批层次上停止了传染方式是在栈/当前堆上的某个未对齐内存位置做一次内存到内存拷贝。当前停止渗透的位置有些随意——例如把已知承载 8k 页的Bytes替换为 8k 大小的IoBuffer可能是更合理的做法。需要澄清的是IoBufAligned/IoBufAlignedMut并不能防护文件偏移对齐违规和I/O 大小违规这两类运行时错误。对此更高层的构造负责兜底读路径ChunkedVectoredReadBuilder与mod vectored_dio_read保证读发生在对齐偏移、且大小是合适的倍数写路径BufferedWriter只在start_offset N*capacity的偏移、以 capacity 的整数倍长度写入见其文档注释。一个值得注意的工程决策这些类型无论是否启用 direct IO 都会使用。某些场景下这会为 buffered IO 增加不必要的开销例如所有 memcpy 都被放大为 512 的倍数但在仍使用 buffered IO 的过渡期实践上并未发现明显影响。源码中还提供了对齐校验的兜底VirtualFileInner::validate_direct_iopageserver/src/virtual_file.rs会在testingfeature 或测试构建下对每一次read_at/write_at校验缓冲区地址对齐、偏移对齐与大小倍数断言信息直接输出十六进制余数便于定位违规调用点。6.4 配置与特性开关virtual_file_io_mode前文已述所有 VirtualFile 使用者都被改造成始终遵守 direct IO 对齐与大小倍数要求。要真正启用 direct IO唯一需要做的是在open系统调用 / io_uring 操作中设置O_DIRECT标志。是否设置O_DIRECT取决于三个因素创建/打开 VirtualFile 实例所用的VirtualFile APIvirtual_file_io_mode配置标志OpenOptions 的read和/或write标志。只有后缀为_v2的 VirtualFile API 才可能根据另外两个因素打开O_DIRECT其他 API 永远不会使用O_DIRECT。RFC 承认这个命名不好应该叫_maybe_direct_io。之所以要新增这些 API是因为虽然所有代码都用 VirtualFile但实现与发布是分阶段进行的读路径 → InMemoryLayer → 写路径而在 VirtualFile 层无法得知某个实例处于读路径、InMemoryLayer 还是写路径。_v2API 根据virtual_file_io_mode标志与 OpenOptions 的read/write标志决定是否设置O_DIRECT。这一逻辑的源码实现正是 pageserver/src/virtual_file.rs 的open_v2/open_with_options_v2let mode get_io_mode(); let direct match (mode, open_options.is_write()) { (IoMode::Buffered, _) false, (IoMode::Direct, false) true, (IoMode::Direct, true) false, (IoMode::DirectRw, _) true, }; open_options open_options.direct(direct);而IoMode枚举定义在 libs/pageserver_api/src/models.rspub enum IoMode { /// Uses buffered IO. Buffered, /// Uses direct IO for reads only. Direct, /// Use direct IO for reads and writes. DirectRw, }且IoMode::preferred()返回DirectRw——即当前仓库默认偏好读写全部 direct IO。最终的运行时行为可以用 RFC 中的这张表精确描述| 使用方 | OpenOptions |virtual_file_io_modebuffered|virtual_file_io_modedirect|virtual_file_io_modedirect-rw| |-|-|-|-|-| |DeltaLayerInner| read | () | O_DIRECT | O_DIRECT | |ImageLayerInner| read | () | O_DIRECT | O_DIRECT | |InMemoryLayer| read write | () | ()* | O_DIRECT | |DeltaLayerWriter| write | () | () | O_DIRECT | |ImageLayerWriter| write | () | () | O_DIRECT | |download_layer_file| write | () | () | O_DIRECT |其中InMemoryLayer标了*曾有一段时期它在direct模式下也使用 O_DIRECT——那是第一版BufferedWriter实现并发布、用于InMemoryLayer与download_layer_file的时期当时只对InMemoryLayer响应v_f_io_mode。后来引入direct-rw并把剩余写路径切换到BufferedWriter才形成上表所示格局。RFC 还提醒这种在 VirtualFile 内部做特性开关的方式使 VirtualFile 越来越不像一个通用的 POSIX 文件访问抽象。例如启用direct-rw后打开任何 VirtualFile 都会带 O_DIRECT不可能不带见open_with_options_v2的(IoMode::DirectRw, _) true分支。在配置接入层面virtual_file_io_mode是 pageserver/src/config.rs 的字段pageserver 启动时打印starting with virtual_file IO modepageserver/src/bin/pageserver.rs并通过virtual_file::init全局生效I/O 引擎std-fsvstokio-epoll-uring同样是全局选择见 pageserver/src/virtual_file/io_engine.rs。6.5 两种 I/O 引擎与 ECANCELED 重试pageserver/src/virtual_file/io_engine.rs 展示了 direct IO 落地的第二维抽象IoEngine枚举支持StdFs与TokioEpollUring。Linux 上首选 io_uring 引擎feature_test会做启动探测失败则回退StdFs并给出原因。值得留意的一个实现细节是retry_ecanceled_once在测试中观察到SIGTERM 立即停止正在摄取数据的 pageserver 时buffered writer 偶尔会以 ECANCELED 失败疑似 io_uring 处理被移交的异步工作io-wq与信号之间存在竞态因此对幂等的 VirtualFile 操作失败一次 ECANCELED 后会延迟 100ms 重试一次——这是对齐之外的另一个为 direct IO 服务的健壮性设计。七、正确性验证内存安全与 EINVAL 防线本项目主要存在两类正确性风险IoBuffer/IoBufferMut实现中的内存安全问题——这些类型暴露的 API 与bytescrate 或Vec的 API 大体相同因不满足对齐/大小倍数要求而导致的运行时错误→ 宕机/不可用——读路径上表现为 EINVAL。很遗憾Neon 没有在cargo miri下运行 pageserver 的基础设施因此内存安全问题依赖仔细的同行评审。而对齐要求Neon 会在测试构建中做生产级断言即前文提到的validate_direct_io但这些断言是事后补加的——实际发布前的验证发生在 staging 和 pre-prod 环境。最终direct/direct-rw被启用到 Rust 单元测试和回归测试套件中。RFC 作者回忆从未出现一例由对齐/大小倍数违规导致的 staging/pre-prod/production 错误说明开发者的测试质量足够好。八、性能验证基准测试如何反向推动架构读路径在 staging 与 pre-prod 经历了多轮基准测试迭代。早期实现中基准测试确实暴露了性能回退——正是这些性能测试促使团队实现批处理batching与并发 IOconcurrent IO以避免不可接受的回退。也就是说性能验证不只是验收环节而是直接塑造了最终架构的一部分。写路径的验证则快得多因为bench_ingest基准见 pageserver/benches/bench_ingest.rs覆盖了写路径上数量较少的全部访问模式。九、未来工作RFC 列出的小到大的后续工作如下更完整的清单见对应 Epic 的 Follow-Ups 一节读路径PS PageCache 命中率是解锁并发 IO 与随机读合理延迟的关键。与其被动地调整 PS PageCache 大小不如估算所需大小并可能用它驱动 StorageController 对 shard 的放置决策……除非彻底移除 PS PageCache改用更专门的缓存来缓存索引块。即便如此工作集估算对确定缓存策略依然有帮助。写路径BlobWriter及其使用者可以改回借用式borrowedAPI……除非实现大写入的绕过模式bypass mode对应 issue #10101代码注释中也有呼应本次改造引入的TempVirtualFile可以把更多常见用法内化减少围绕virtual_file_io_mode的条件编译。两者共同一个性能模拟模式即使底层存储更快也把 VirtualFile 操作的延迟填充到典型 NVMe 延迟水平避免开发机与低负载基准机上出现误导性的好成绩。不过在微秒量级填充延迟并不简单。杂项完成对 VirtualFile 范围的收窄使其真正只限于核心数据路径的读写。读取/写入 pageserver 配置、location config、heatmap 等应使用其他包中的 APIVirtualFile::crashsafe_overwrite与VirtualFile::read_to_string是清理的良好切入点。十、附录为什么内核页缓存在高租户密度下低效正文曾断言内核页缓存在高租户密度下低效附录给出了详细论证。Pageserver 从 Compute 收到的负载本质上是 Compute 端缓存未命中的部分——要么是顺序扫描要么是随机读。随机读工作负载直接导致缓存抖动thrashing一台密集打包的 Pageserver NVMe 盘如im4gn.2xlarge容量约为可用 DRAM 的100 倍。此时让内核页缓存去缓存数据块完全是浪费。顺序读工作负载只有在这些页刚被更新过尚无 image 层且在时间/LSN 空间上彼此接近时才能受益。这种情况下这些更新的 WAL 记录很可能位于同一个 delta 层块上Compute 做顺序扫描时会连续发出针对这些页的单页请求Pageserver 处理该序列中的第二个请求时会命中同一个 delta 层块的内核页缓存。RFC 承认这种对内核页缓存的依赖对顺序扫描性能是显著的但解决方案应放在比通用数据块缓存更高的层面要么为这类 delta 层块加一个小的、按连接隔离的 LRU 缓存要么把这些顺序请求合并成更大的 vectored get 请求——它被设计为绝不会重复读同一个块从而把 delta 层块的读延迟摊还到 vectored get 的批次大小上目前最多 32。至于 Pageserver 内部确有顺序访问的工作负载compaction、image 层生成它们不延迟敏感且可以在 page_service 协议约束之外做批量访问image 层生成根本不需要重建 image因此可以使用完全不同的访问方式compaction 可以用 k-way 合并迭代器自带内部缓冲/预取。延伸阅读本文对应的并发 IO 设计见 docs/rfcs/2025-04-30-pageserver-concurrent-io-on-read-path.mdvectored get 的历史背景见 docs/rfcs/030-vectored-timeline-get.md核心实现文件为 pageserver/src/virtual_file.rs、pageserver/src/virtual_file/io_engine.rs、pageserver/src/virtual_file/owned_buffers_io/write.rs配置定义在 pageserver/src/config.rsIoMode枚举在 libs/pageserver_api/src/models.rs。【免费下载链接】neonNeon: Serverless Postgres. We separated storage and compute to offer autoscaling, code-like database branching, and scale to zero.项目地址: https://gitcode.com/GitHub_Trending/ne/neon创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表