大云海山数据库(He3DB)里有个很容易被忽略、但实际在存储计算分离架构中起关键作用的模块,叫 FileCache。我第一次看这个名字,以为只是普通的文件系统级缓存,后来把整体读写链路完整过了一遍才发现,它在 shared_buffers 和远端存储之间补了一层真正的二级页缓存。这篇就系统地讲一讲 FileCache 到底解决了什么问题、内部是怎么设计的、有哪些值得注意的坑,以及在实际调优时该怎么判断和取舍。比较适合正在研究数据库内核、或者在做国产数据库存储引擎选型和性能优化的人参考。
1. FileCache 要解决什么问题
1.1 内存缓存的天花板不是内存条,而是存储链路
传统 PostgreSQL 的缓存体系大家都熟悉:shared_buffers 里放热数据页,通过 clock sweep 之类的近似 LRU 算法管理,读路径先查这里,没有命中再去数据文件抓页。这套机制本身没有问题,问题出在“冷热数据规模”和“物理存储位置”上。
单机部署时,shared_buffers 没命中就直接读本地磁盘,即使磁盘慢一点,整个链路仍然是可控的。但在云上存储计算分离架构里,数据库计算节点和真实数据存储之间隔了网络,哪怕走 RDMA 或者高速存储网络,单次读页的延迟和吞吐也比不了本地内存访问。这种场景下,如果把所有压力都推给远端存储,缓存命中率稍微低一点,整个数据库的响应时间和吞吐量就会明显恶化。
而 shared_buffers 本身又不可能无限调大。内存贵是一方面,更关键的是数据库的缓冲池管理是有代价的:buffer 数量越大,查找哈希表、维护淘汰链表、处理淘汰和刷脏的 CPU 开销都在涨,超过一定规模后,加内存带来的收益会被管理成本吃掉很大一部分。所以一般的 PG 实例把 shared_buffers 设置到机器内存的 1/4 到 1/2 已经算很激进了,再大就容易出现长路径扫描把整个缓冲池冲垮、checkpoint 刷盘毛刺变高等问题。
1.2 存储计算分离带来的新瓶颈
He3DB 这种云原生数据库走的是存储计算分离路线,好处是存储可以独立扩展、成本低,但代价就是计算节点不再天然拥有数据文件的本地副本。每一次真实的读 miss 都要通过网络请求远端存储,这个成本比传统本地磁盘还要不稳定,因为可能受网络抖动、存储侧排队、多租户争抢等因素影响。
这时候你会面临一个很现实的选择:要不要在计算节点本地放盘,承担一层额外的缓存?如果放,放什么盘?如果用内存,成本和上面的管理开销挡着。如果完全依赖远端存储,性能不稳定,尤其是遇到大范围扫描、批量分析这种查询时,缓存 miss 会成片出现。
FileCache 的思路其实就是:充分利用计算节点上的本地高速存储,比如 NVMe SSD,把它组织成数据库页感知的二级缓存。它的热度档次介于内存和远端存储之间:容量比内存大一个数量级,访问延迟和吞吐比远端存储稳定得多,成本又远低于内存。它不是简单的操作系统页缓存,而是直接从数据库页的粒度去管理数据块的存放位置、命中状态和淘汰顺序,能感知页面大小、脏页状态、事务一致性边界,这是普通文件级缓存做不到的。
1.3 FileCache 在整体架构里的价值定位
一句话总结 FileCache 的定位:它是内存缓冲池的延伸,也是远端存储的挡箭牌。
当一个数据页在 shared_buffers 中 miss 时,原来的逻辑是直接去远端存储抓页。引入 FileCache 后,读路径变成了先去 FileCache 查一级,如果有就直接把数据页加载进内存缓冲池,避免了远端网络 IO;如果没有,才真正访问远端存储,并把读回来的页顺带写入 FileCache,方便下一次命中。
这个设计对整个系统的性能模型有很直接的意义:
- 提升缓存体系的总命中率,而不只是把宝押在 shared_buffers 那几十 GB 上。
- 吸收突发的扫描型负载,避免全表扫描、大批量分析查询把内存缓冲池冲垮的同时,把远端存储的 IOPS 打满。
- 让存储扩容和数据缓存解耦,存储可以很大很便宜,热数据却可以就近放在计算节点。
理解 FileCache,重点不是记它有哪些参数,而是理解它在这个架构里到底扮演什么角色。把它当成二级缓冲池来理解,后面的机制细节就顺了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. FileCache 的整体设计与数据布局
2.1 文件映射与页级索引
FileCache 既然要从数据库页粒度管理缓存,就不可能直接拿整个文件丢给操作系统去缓存,那样完全无法感知哪些页被访问过、哪些
