Neon compute GetPage

计算节点(compute)执行 getpage 请求的完整调用栈,从上到下:

1. 从 Postgres 内核入口

text 复制代码
PostgreSQL buffer manager
  └─ bufmgr.c: ReadBufferExtended() / InitBufferPage()
      └─ smgrread() / smgrreadv()   ← 分发到 Neon 的 smgr hook

2. Neon smgr 层

pgxn/neon/pagestore_smgr.c

neon_read() (PG≤16) // 单块读取

neon_readv() (PG≥17) // 向量化读取(批量)

│

├─ 1 communicator_prefetch_pump_state()

│ └─ communicator.c:468

│ 从 TCP 接收缓冲中 drain 已到达的 prefetch 响应

│

├─ 2 communicator_prefetch_lookupv()

│ └─ communicator.c:1049

│ 在 prefetch ring buffer (hash table) 中查找是否已有缓存的页

│ 命中 → memcpy 到 buffer,返回;未命中则继续

│

├─ 3 lfc_read / lfc_readv_select()

│ └─ file_cache.c

│ 查 Local File Cache(本地磁盘缓存)

│ 命中 → memcpy,返回;未命中则继续

│

└─ 4 neon_read_at_lsn() / communicator_read_at_lsnv()

└─ communicator.c:2093

└─ 见下方详细链路

核心请求发送/接收链路

pgxn/neon/communicator.c

communicator_read_at_lsnv()

│

├─ prefetch_register_bufferv() // 注册到 ring buffer

│ └─ prefetch_do_request() // 填充 LSN、构建 NeonGetPageRequest

│ └─ page_server->send() // 发送请求

│ └─ libpagestore.c:1135

│ nm_pack_request() // 序列化(pq_send...)

│ PQputCopyData() // 发到 Pageserver TCP

│

├─ prefetch_wait_for(ring_index) // 阻塞等待响应

│ └─ prefetch_flush_requests() // 如果有未 flush 的请求先 flush

│ └─ prefetch_read(slot) // 从 Pageserver 接收响应

│ └─ page_server->receive()

│ └─ libpagestore.c:1219

│ call_PQgetCopyData() // 异步 read,含超时/断连逻辑

│ nm_unpack_response() // 反序列化

│

└─ 处理响应

├─ T_NeonGetPageResponse → memcpy(page, buffer, BLCKSZ)

├─ T_NeonErrorResponse → ereport(ERROR)

└─ prefetch_set_unused() // 释放 ring slot

Pageserver 端处理

pageserver/src/page_service.rs

PageStream handler (每连接一个线程)

└─ handle_getpage_request()

└─ tenant/timeline::get_page_at_lsn()

├─ 查 in-memory delta / image layer

├─ 查 s3 remote storage(需拉取时)

└─ WAL redo(pageserver 侧 walredo,非 compute 侧)

└─ 对旧版本 page image 应用 WAL diff

关键设计要点

  1. Prefetch Ring Buffer:compute 端维护一个最多 readahead_buffer_size(默认 128)个 slot 的环形缓冲区,支持异步 pipelining。

  2. LSN 语义:请求携带 (request_lsn, not_modified_since) 两个 LSN,primary 端 request_lsn=UINT64_MAX(最新),replica

端=replay_lsn。

  1. 三重缓存:prefetch ring → LFC(Local File Cache,本地磁盘)→ 远程 Pageserver。

  2. 协议:基于 libpq COPY 协议的 pagestream_v3,每个 request 带 reqid 用于匹配响应。

  3. 分片:get_shard_number() 按 relNode + block/stripe_size 哈希到不同 shard(Pageserver)。

相关推荐
对空六课3 小时前
两个统计平台数据不同:用同一时间窗和事件样本做差异定位
大数据·前端·网络·数据库
wno7048 小时前
postgresSQL压缩版安装与基本命令
数据库·postgresql
聚美智数10 小时前
智能结构化识别-图片结构解析-图像结构化解析-照片结构化解析
android·java·数据库
瀚高PG实验室10 小时前
瀚高数据库常用系统表查询入门指南(附常用SQL)
数据库·sql·postgresql·瀚高数据库
ben9518chen10 小时前
mysql数据库基本操作
数据库·mysql
2603_9651481110 小时前
电商选品调研:通过关键词搜索API获取市场热销商品,结合商品详情API分析竞品卖点与定价
开发语言·数据库·人工智能·python·信息可视化
土豆.exe11 小时前
nf_tables 的 verdict 被读了两次:一个十年未闭合的位域错配
java·数据库·网络安全·缓存
缓慢更新11 小时前
表单引擎的存储建模:宽表与文档库的取舍
运维·服务器·网络·数据库·低代码·oracle·适配器模式
Web3&Basketball13 小时前
实测Gemini4Argon:ERQA 87%登顶长程任务榜
数据库·python·大模型·多模态·gemini·长上下文·推理优化