PostgreSQL 使用两层数据缓存
普通缓冲式关系文件读写中,PostgreSQL 的共享缓冲位于操作系统文件缓存之上。命中共享缓冲时,不需要再次通过下面的文件读取路径;没有命中时,也可能由操作系统直接从内存提供数据。同一文件内容可以在两层各有副本,所以这里并不承诺数据库与内核共用同一份物理内存,更不能把两层缓存理解为完全没有重复占用。mmap 共享内存配置针对共享区域,不能据此推断普通表文件被直接内存映射。 坏掉的样子 执行计划里的共享读取计数,表示块被读入 PostgreSQL 缓冲池,不证明存储设备实际执行了一次物理读取。如果把每次共享读取都算成磁盘未命中,已经温热的操作系统缓存也会被误认为冰冷,从而引出错误的内存配置判断。同样,第二次查询更快,也不能独自说明究竟是哪一层缓存发生了变化,或是否只是其他执行条件改变。 缓存驻留是临时状态。其他查询可能挤走页面,工作集超过容量时,也不能指望所有页永久留在内存里;关系扫描采用的缓冲策略还可能影响复用方式。因此应使用已知大小的工作集、检查实际访问路径,并把耗时结论与真正观察到的缓存层事实分开。没有设备层测量,就不能仅凭一个计划字段宣布磁盘速度发生了变化。 Postgres 有两层缓存:机制图。 快速验证 使用 PostgreSQL 18 可丢弃实例和超级用户,确保附带的缓冲观察与预热扩展可安装、且尚未安装。保留的较早运行使用 PostgreSQL 18.6 单用户后端及一百二十八兆字节共享缓冲。本次来源复核没有重跑;psql 传输与并发活动仍未验证。下面的数据可以轻松放下。通过一个全新的 psql -X -v ON_ERROR_STOP=1 会话按顺序执行。关系驱逐函数用于开发测试,这里只针对隔离实验创建的对象操作: CREATE SCHEMA cache_layer_demo; CREATE EXTENSION pg_buffercache WITH SCHEMA cache_layer_demo; CREATE EXTENSION pg_prewarm WITH SCHEMA cache_layer_demo; CREATE TABLE cache_layer_demo.items (id integer PRIMARY KEY,payload text) WITH (autovacuum_enabled=false); INSERT INTO cache_layer_demo.items SELECT g,repeat(md5(g::text),10) FROM generate_series(1,12000) AS g; VACUUM (ANALYZE) cache_layer_demo.items; CREATE VIEW cache_layer_demo.residency AS SELECT count(*) AS heap_buffers FROM cache_layer_demo.pg_buffercache WHERE reldatabase=(SELECT oid FROM pg_database WHERE datname=current_database()) AND relfilenode=pg_relation_filenode('cache_layer_demo.items') AND relforknumber=0; SET max_parallel_workers_per_gather=0; SELECT pg_relation_size('cache_layer_demo.items')/current_setting('block_size')::int AS heap_blocks; SELECT heap_buffers>0 AS initially_buffered FROM cache_layer_demo.residency; 本次构建的主堆有五百四十六个块,初始缓存比较返回真。观察视图限制数据库、文件标识与主分支,并假定这个新建实验只使用默认表空间。通用监控还必须区分表空间,因为文件编号不是数据库内跨表空间唯一的物理身份。每个缓冲条目的观察是一致的,但整个视图并不是同一瞬间的原子缓存快照。接着,仅驱逐这个关系的共享缓冲: ...