当前位置: 首页 > article >正文

neon源码分析(5)计算层使用slru的一些问题

1. PG 原生 SLRU 是什么SLRU 用来保存事务相关的小页面文件常见目录pg_xact pg_multixact/members pg_multixact/offsets一个 SLRU page 是 8KB。一个 SLRU segment 通常包含 32 个 page1 segment 32 * 8KB 256KB 例子pg_xact/0001 page 0 8KB page 1 8KB ... page 31 8KBPG 原生逻辑假设文件在本地磁盘上。2. Neon 为什么改造 SLRUNeon 不希望把完整pg_xact/pg_multixact文件都放进 basebackup否则 endpoint 启动和传输成本会变大。所以 Neon 的策略是本地 SLRU segment 不存在 | v 从 pageserver 按需下载该 segment | v 写成本地 SLRU 文件 | v 继续走 PG 原生 SLRU 读逻辑也就是说Neon 改造的是compute 本地 SLRU 文件缺失时的 fallback 路径。3. Compute 侧关键改造关键文件neon/vendor/postgres-v17/src/backend/access/transam/slru.cNeon 新增核心函数staticintSimpleLruDownloadSegment(SlruCtl ctl,intpageno,charconst*path)关键逻辑/* page 超过 latest_page_number不尝试下载 */if(ctl-PagePrecedes(pg_atomic_read_u64(shared-latest_page_number),pageno))return-1;segnopageno/SLRU_PAGES_PER_SEGMENT;bufferpalloc(BLCKSZ*SLRU_PAGES_PER_SEGMENT);n_blockssmgr_read_slru_segment(dummy_smgr_rel,path,segno,buffer);if(n_blocks0){fdOpenTransientFile(path,O_RDWR|O_CREAT|PG_BINARY);pg_pwrite(fd,buffer,n_blocks*BLCKSZ,0);}含义由pageno算出segno。调用 Neon 的smgr_read_slru_segment()。从 pageserver 获取该 SLRU segment 的内容。写成本地文件比如pg_xact/0001。返回 fd后续 PG 继续用普通pread()读目标 page。和 PG 原生的差异原生 PGopen pg_xact/0001 失败errno ENOENT | -- recovery 中可能读 zero page | -- 非 recovery报错Neonopen pg_xact/0001 失败errno ENOENT | v SimpleLruDownloadSegment() | -- 下载成功写本地文件然后继续读 | -- 下载失败回到 PG 原生处理逻辑影响的主要路径SimpleLruDoesPhysicalPageExist() SlruPhysicalReadPage()4. SLRU 请求路径端到端调用链SlruPhysicalReadPage() | | 本地 SLRU segment 文件不存在 v SimpleLruDownloadSegment() | v smgr_read_slru_segment() | v neon_read_slru_segment() | | 计算 request_lsn / not_modified_since v communicator_read_slru_segment() | v pageserver GetSlruSegment(kind, segno, lsn) | v pageserver 按 8KB block reconstruct segment | v 返回 n_blocks * 8KB | v compute 写成本地 pg_xact/0001关键 compute 文件neon/pgxn/neon/pagestore_smgr.c neon/pgxn/neon/communicator.c5. SLRU 请求 LSN 怎么算关键函数neon/pgxn/neon/pagestore_smgr.c neon_read_slru_segment(...)关键逻辑if(RecoveryInProgress()){request_lsnGetXLogReplayRecPtr(NULL);if(request_lsnInvalidXLogRecPtr)request_lsnGetRedoStartLsn();request_lsnnm_adjust_lsn(request_lsn);}elserequest_lsnUINT64_MAX;not_modified_sincenm_adjust_lsn(GetRedoStartLsn());含义场景request_lsnrecovery 中当前 replay LSNrecovery 刚开始还没有 replay LSNredo start LSN非 recoveryUINT64_MAX表示取最新not_modified_since使用 basebackup 的 redo start LSN。原因未下载到本地的 SLRU segment如果要被本地修改必须先下载下载后也不会被本地淘汰。所以对于尚未下载的 segment可以认为它自 basebackup LSN 以来没有被本地改过。6. SLRU 页面有没有版本有但不是存在 SLRU page header 里。普通 heap/index page 自身有 page header例如pd_lsn。SLRU page 没有类似普通 page 的pd_lsn。Neon 的 SLRU 版本保存在 pageserver timeline/layer 中slru_block_to_key(kind, segno, blkno) LSN这里的 LSN 是 WAL LSN。更具体地说某条 WAL record 修改了 SLRU那么该修改在 pageserver 中一般以WAL record 的 end LSN作为版本点。WAL record 范围[start_lsn, end_lsn) record 修改 CLOG page | v pageserver 写入 slru_block_to_key(Clog, segno, blkno) end_lsn所以之前说的SLRU 的版本 LSN 是 WAL LSN这里的 LSN 可以理解成该 WAL record 的最后位置 1 也就是 WAL record end LSN例子WAL record R: start_lsn 0/5000100 end_lsn 0/5000188 R 修改了 pg_xact/0001 的第 5 个 SLRU page pageserver 中的版本 slru_block_to_key(Clog, 1, 5) 0/5000188当 compute recovery 到0/5000188请求GetSlruSegment(Clog, segno1) 0/5000188应该可以看到这条 WAL record 对 CLOG 的修改。7. Neon 按什么粒度保存 SLRU需要区分两个粒度compute - pageserver 请求粒度segment pageserver 内部保存粒度8KB block/page也就是说compute 请求时是GetSlruSegment(kind, segno)返回大小是n_blocks * 8KB 通常最大 32 * 8KB 256KB但 pageserver 内部不是把完整 256KB 文件作为一个 value 保存而是拆成 8KB blockslru_block_to_key(kind, segno, 0) - 8KB slru_block_to_key(kind, segno, 1) - 8KB ... slru_block_to_key(kind, segno, 31) - 8KB例子compute 要读 pg_xact/0001 | v 请求 GetSlruSegment(Clog, 1) | v pageserver 内部读取 size key 得到 n_blocks 32 block key 0..31 各取 8KB | v 拼成 256KB 返回 compute8. pageserver 为什么有三类 SLRU key关键文件neon/libs/pageserver_api/src/key.rs三类 keykey 类型函数是否带 segno是否带 blkno作用directoryslru_dir_to_key(kind)否否记录某类 SLRU 有哪些 segmentsegment sizeslru_segment_size_to_key(kind, segno)是否记录某 segment 有多少 blockdata blockslru_block_to_key(kind, segno, blknum)是是保存实际 8KB page 或 WAL delta8.1 directory key源码pubfnslru_dir_to_key(kind:SlruKind)-Key{Key{field1:0x01,field2:matchkind{SlruKind::Clog0x00,SlruKind::MultiXactMembers0x01,SlruKind::MultiXactOffsets0x02,},field3:0,field4:0,field5:0,field6:0,}}说明每个 SlruKind 一个 directory key。 key 形状基本固定不带 segno / blkno。 value 是 segment 集合。例子slru_dir_to_key(Clog) - value {0, 1, 2, 3}8.2 segment size key源码pubfnslru_segment_size_to_key(kind:SlruKind,segno:u32)-Key{Key{field1:0x01,field2:kind_code,field3:1,field4:segno,field5:0,field6:0xffff_ffff,}}说明每个 kind segno 一个 size key。 field6 0xffffffff 是 sentinel表示这是 size key不是普通 block key。 value 是 nblocks。例子slru_segment_size_to_key(Clog, 1) - value 328.3 data block key源码pubfnslru_block_to_key(kind:SlruKind,segno:u32,blknum:BlockNumber)-Key{Key{field1:0x01,field2:kind_code,field3:1,field4:segno,field5:0,field6:blknum,}}说明每个 kind segno blknum 一个 data key。 value 是 8KB page image 或 WAL delta。例子slru_block_to_key(Clog, 1, 0) - pg_xact/0001 第 0 个 8KB page slru_block_to_key(Clog, 1, 31) - pg_xact/0001 第 31 个 8KB page三类 key 的关系图pg_xact/0001 in pageserver Directory key: slru_dir_to_key(Clog) value contains segment 1 Segment size key: slru_segment_size_to_key(Clog, 1) value 32 Data block keys: slru_block_to_key(Clog, 1, 0) - 8KB slru_block_to_key(Clog, 1, 1) - 8KB ... slru_block_to_key(Clog, 1, 31) - 8KB回答前面的问题前两种 key 是不是写死的 不是完全写死 - directory key只跟 kind 有关所以对某个 kind 来说是固定的。 - segment size key跟 kind segno 有关不带 blkno。 - data block key跟 kind segno blkno 有关。9. pageserver 如何拼出 SLRU segment关键文件neon/pageserver/src/pgdatadir_mapping.rs核心函数get_slru_segment(kind,segno,lsn,ctx)关键代码letn_blocksself.get_slru_segment_size(kind,segno,Version::at(lsn),ctx).await?;letkeyspaceKeySpace::single(slru_block_to_key(kind,segno,0)..slru_block_to_key(kind,segno,n_blocks),);letqueryVersionedKeySpaceQuery::uniform(batch,lsn);letblocksself.get_vectored(query,io_concurrency.clone(),ctx).await?;for(_key,block)inblocks{letblockblock?;segment.extend_from_slice(block[..BLCKSZasusize]);}流程GetSlruSegment(Clog, 1, lsnX) | v 读 slru_segment_size_to_key(Clog, 1) X | | n_blocks 32 v 读 block key 0..31 X | v 拼成 segment bytes | v 返回 compute10. WAL ingest 如何写 SLRU关键文件neon/pageserver/src/walingest.rs neon/pageserver/src/pgdatadir_mapping.rs事务 commit / abort 会写 CLOGmodification.put_slru_wal_record(SlruKind::Clog,segno,rpageno,NeonWalRecord::ClogSetCommitted{...},)?;MultiXact offsetsmodification.put_slru_wal_record(SlruKind::MultiXactOffsets,segno,rpageno,NeonWalRecord::MultixactOffsetCreate{...},)?;MultiXact membersmodification.put_slru_wal_record(SlruKind::MultiXactMembers,pageno/SLRU_PAGES_PER_SEGMENT,pageno%SLRU_PAGES_PER_SEGMENT,NeonWalRecord::MultixactMembersCreate{...},)?;最终写入 block keyself.put(slru_block_to_key(kind,segno,blknum),Value::WalRecord(rec),);zero page / full image 则写self.put(slru_block_to_key(kind,segno,blknum),Value::Image(img));11. segment 创建、扩展、删除创建 segmentput_slru_segment_creation(kind,segno,nblocks,ctx)做两件事1. 更新 directory key把 segno 加入集合 2. 写 segment size keyvalue nblocks扩展 segmentput_slru_extend(kind,segno,nblocks)主要更新slru_segment_size_to_key(kind, segno) new_nblocks如果中间有 gap会补 zero page。删除 segmentdrop_slru_segment(kind,segno,ctx)做两件事1. 从 directory key 的 segment 集合里移除 segno 2. 删除该 segment 的 size key 和所有 block key删除范围slru_segment_key_range(kind,segno)覆盖slru_block_to_key(kind, segno, 0..) slru_segment_size_to_key(kind, segno)12. 总结一句话Neon compute 缺 SLRU 文件时按 segment 从 pageserver 下载 pageserver 内部按 8KB SLRU block 做版本化存储 版本点是 WAL record end LSN。关键结论1. compute 拉取粒度segment通常最大 256KB。 2. pageserver 保存粒度8KB SLRU block。 3. SLRU page 没有自身 pd_lsn版本在 pageserver key LSN 中。 4. SLRU 的 LSN 是 WAL LSN通常是 WAL record end LSN。 5. 三类 key - dir key记录有哪些 segment - segment size key记录某 segment 有几个 block - data block key记录实际 8KB 数据或 WAL delta最终图WAL ingest | v pageserver timeline | | dir key LSN | size key LSN | block key LSN v compute 读 pg_xact/0001 | | 本地不存在 v SimpleLruDownloadSegment() | v GetSlruSegment(Clog, 1, request_lsn) | v pageserver 读取 size block keys request_lsn | v 拼出 segment bytes | v compute 写本地 pg_xact/0001 | v PG 原生 SLRU 继续读目标 page

相关文章:

neon源码分析(5)计算层使用slru的一些问题

1. PG 原生 SLRU 是什么 SLRU 用来保存事务相关的小页面文件,常见目录: pg_xact pg_multixact/members pg_multixact/offsets一个 SLRU page 是 8KB。一个 SLRU segment 通常包含 32 个 page: 1 segment 32 * 8KB 256KB例子:…...

从2E服务写入超长DID说起:一个案例拆解Autosar UDS诊断中‘非主流’的帧交互流程

从2E服务写入超长DID案例解析Autosar UDS诊断中的多帧交互机制 在汽车电子开发领域,诊断协议的设计与实现往往是系统可靠性的关键所在。当我们谈论UDS(Unified Diagnostic Services)诊断时,大多数开发者首先想到的是常规的单帧请求…...

基于开源LLM框架构建领域对话机器人:从ChatPiXiu到实战应用

1. 项目缘起与定位去年ChatGPT横空出世,相信很多同行和我一样,一边惊叹于其强大的对话能力,一边也在琢磨:这东西的“魔法”我们能不能复现?或者说,有没有可能用开源的方式,打造一个我们自己的、…...

30岁软件测试工程师的出路:不是转管理,而是换赛道

打破“管理独木桥”的迷思在软件测试行业,流传着一条看似顺理成章的晋升路径:做几年技术,然后转型做管理。尤其对于步入30岁的工程师来说,这条路径仿佛成了唯一的救命稻草,仿佛不走上管理岗,职业生涯就会戛…...

千亿企业级存储市场,产品逻辑变了

国家数据局相关数据显示,截止今年3月,我国日均Token调用量已超过140万亿,相比于2024年初增长1000多倍;同时,OpenAI公布数据也显示,其API调用量为每分钟60亿Tokens,月度总量更是达到惊人的260万亿…...

数电3|传输门、三态门、开路门

二、CMOS集成1.传输门2.三态门3.漏极开路门(OD门)...

用Gemini做竞品分析:从截图识别到对比表格生成的完整教程

做竞品分析时,最耗时间的往往不是“分析”,而是前期整理:截图、提取页面信息、归纳功能点、做对比表。现在可以把这部分交给Gemini辅助完成。如果想先对比不同模型的图片理解和表格整理能力,也可以通过 AI模型聚合平台 t。877ai。…...

除了恢复数据,binlog2sql还能这么玩?解锁MySQL二进制日志的3个高级用法

解锁binlog2sql的隐藏技能:MySQL二进制日志的三大高阶应用 MySQL的二进制日志(binlog)是数据库运维中不可或缺的组成部分,它记录了数据库的所有变更操作。大多数开发者对binlog的认知停留在数据恢复层面,而binlog2sql作…...

KORG logue SDK音频开发实战:从DSP原理到嵌入式音乐合成器编程

1. 项目概述:深入KORG logue SDK的音频开发世界如果你是一位嵌入式开发者,同时对音乐合成器抱有浓厚的兴趣,那么“korginc/logue-sdk”这个项目标题,很可能已经让你心跳加速了。这不仅仅是GitHub上的一个代码仓库,它更…...

构建AI Agent技能库:从零到一打造个人与团队的智能体工具箱

1. 项目概述:构建你自己的AI Agent技能库如果你和我一样,每天都要和Claude、Cursor、Codex这些AI助手打交道,那你肯定也遇到过这样的问题:面对浩如烟海的Agent技能,每次需要的时候都得临时去搜,搜到了还得验…...

kode:harness:统一团队AI编码方向的工程框架

1. 项目概述:kode:harness,一个为团队AI编码对齐方向而生的工程框架如果你在一个团队里,发现每个开发者用AI助手写代码时,项目就像被几匹脱缰的野马往不同方向拉扯,那么kode:harness就是那套统一的缰绳和导航系统。这不…...

2025届最火的五大AI学术神器横评

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 在人工智能技艺得以普遍应用的情形下,免费的AI论文生成器具给学术创作予以了便利…...

ACM新版伦理准则解读:从代码到公共福祉的开发者责任转型

1. 从“单打独斗”到“协同共生”:计算伦理更新的时代背景1992年,当ACM上一次修订其伦理准则时,一个开发者单枪匹马写出一款影响深远的软件,还是可能发生的事情。但即便在那个时代,软件开发的方式也已经在悄然改变。自…...

2025届毕业生推荐的五大降重复率平台推荐

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 基于先进深度学习算法构建的AI写作工具,具备强大功能,能为用户提供多…...

JavaScript 打开小窗口的实现方法

function openCenteredWindow(url ) {const width 600;const height 400;// 计算居中位置const left Math.round((screen.width - width) / 2);const top Math.round((screen.height - height) / 2);window.open(url,"_blank",width${width},height${height},lef…...

00后下场整顿Agent:啥都不学就能用好AI,这才是正确打开方式

允中 发自 凹非寺量子位 | 公众号 QbitAIAI圈有个怪现象:模型越来越强,确实是好事;但随着AI用法越发多样,用起来的门槛却越来越高。ChatGPT、Gemini、Claude……即便这些模型的能力已经够强了,但真到上手干活的时候&am…...

一年磨一剑,今年最炸机器人Demo来了!1亿美元种子轮团队出手,单个模型解锁单手打蛋解魔方弹钢琴

henry 发自 凹非寺量子位 | 公众号 QbitAI看过的人已经傻眼了,因为这可能是今年为止最炸的机器人demo。刚刚,Genesis AI发布首个机器人基础模型GENE-26.5,让机器人能自主打鸡蛋,拧试管盖,弹钢琴,玩魔方&…...

教育机构在AI课程实验中采用Taotoken管理学生模型调用的实践

教育机构在AI课程实验中采用Taotoken管理学生模型调用的实践 在高校或职业培训机构的AI课程中,让学生亲手调用大模型API完成实验是提升实践能力的关键环节。然而,直接让学生使用个人账户或共享密钥会带来成本不可控、权限混乱、行为难以追溯等一系列管理…...

2026 生物医药融资技术趋势:AI 制药、CGT、ADC 成资本主战场

一、核心数据截至 2026 年 5 月 7 日,全球生物医药行业融资活动呈现显著回暖态势,中国市场表现尤为突出。根据医药魔方等行业报告,2026 年第一季度 1-3 月核心数据如下:进入 4 月,市场热度不减,深圳泽安生物…...

坤和静界·春藤计划:一场针对青少年休学的标准化心理干预实

青少年休学问题正在变得不容忽视。有调查显示,在患有抑郁症等精神障碍的儿童青少年中,超过半数有过休学经历,首次休学的平均年龄只有13.74岁。而在休学之后,近六成家长对复学感到焦急,却找不到系统性的解决方案。 市面…...

为AI智能体注入Power BI专家级能力:OpenClaw技能包全解析

1. 项目概述:为AI智能体注入Power BI专家级能力 如果你正在探索如何让AI助手(或者说,智能体)真正理解并操作像Power BI这样复杂的企业级商业智能工具,那么你很可能已经遇到了瓶颈。传统的提示词工程往往只能让大语言模…...

CC-Switch 下载、安装与使用全指南(2026.5.7--最新v3.14.1)

CC-Switch 下载、安装与使用全指南(2026最新v3.14.1) CC-Switch 是跨平台AI API一键切换管理器,统一管理 Claude Code、DeepSeek、Kimi、智谱、MiniMax 等模型,不用改环境变量、不用手动改配置文件,VS Code/终端一键切…...

开源 | 我是怎么用 ai-memory 让 Cursor 每次开新对话都自动知道项目背景的

开源 | 我是怎么用 ai-memory 让 Cursor 每次开新对话都自动知道项目背景的开源地址:https://github.com/hyxnj666-creator/ai-memory npm:npx ai-memory-clilatest(内置免费模型,无需配置 API Key) 协议:M…...

基于AI与RPA的智能求职自动化系统设计与实现

1. 项目概述:当求职自动化遇上AI与RPA最近在技术社区里,看到不少朋友在讨论一个叫auto_job__find__chatgpt__rpa的项目。光看这个标题,就让我这个在招聘和自动化领域摸爬滚打了十来年的老鸟眼前一亮。这名字拆开来看,auto_job_fin…...

SQL Server如何实现编写表与字段注释_Navicat兼容操作步骤

ASSM表空间不能设为MANUAL,因LMT不支持手动段管理,10g执行SEGMENT SPACE MANAGEMENT MANUAL会报ORA-12913;新建表空间必须用AUTO,FREELIST在LMT下无效。ASSM 表空间为什么不能关自动段管理本地管理表空间(lmt&#xff…...

用SystemVerilog玩转约束:除了`inside`和`dist`,你还能这样写条件约束

用SystemVerilog玩转约束:超越基础语法的创意实践 在芯片验证的世界里,随机测试就像一把瑞士军刀——它能帮你发现那些手工测试难以触及的角落。但真正的高手都知道,随机测试的质量取决于约束的质量。当你在验证PCIe或DDR这类复杂协议时&…...

5分钟搞懂BERT tokenizer:用encode_plus为你的NLP模型准备‘标准餐’(附PyTorch/TF代码适配)

5分钟搞懂BERT tokenizer:用encode_plus为你的NLP模型准备‘标准餐’(附PyTorch/TF代码适配) 想象一下,你正在为一位挑剔的米其林大厨准备食材——每一片蔬菜的厚度、每一块肉的纹理都必须精确到毫米级。在自然语言处理&#xff0…...

AI编程助手选型指南:从GitHub Awesome清单到高效开发实践

1. 项目概述与价值定位最近在GitHub上闲逛,又发现了一个宝藏仓库——CodandoTV维护的“awesome-ai-coding-assistants”。作为一名在代码堆里摸爬滚打了十多年的老开发,我第一眼看到这个标题就来了兴趣。这不仅仅是一个简单的工具列表,它更像…...

OpenClawWeChat:基于Wechaty的插件化微信机器人开发与部署实战

1. 项目概述与核心价值最近在折腾微信机器人,想找一个能稳定、灵活地处理消息,还能对接各种外部服务的方案。市面上工具不少,但要么功能单一,要么配置复杂,要么就是稳定性堪忧,动不动就被风控。直到我深度体…...

SQL中如何对聚合后的数据进行二次计算_GROUP BY与算术组合

GROUP BY后不能在同层SELECT或HAVING中直接复用聚合结果做二次计算,需用子查询或CTE先聚合再运算,并注意NULLIF防除零、COALESCE处理空值、ROUND控制精度及WHERE/HAVING分工。GROUP BY 后不能直接用聚合字段做算术运算写 SELECT SUM(a) * 2 FROM t GROUP…...