近日,由 Open Source Hong Kong(开源香港)主办的月度开发者沙龙在香港顺利落幕。瀚高股份资深 PostgreSQL 内核研发专家、PG 19 版本核心贡献者周煦能受邀带来全英文主题演讲《The Hitchhiker’s Guide to PostgreSQL Hacking: Don’t Panic, Just Start Small》。
针对广大开发者“想参与 PG 开源贡献、却苦于代码库庞大、社区门槛高无从下手”的普遍难题,本次分享给出了一套新手友好、可落地、循序渐进的开源贡献方法论,从心态、入门路径、社区工作流到真实内核案例,完整拆解从零到一的 PostgreSQL 黑客成长之路。
以下为本次演讲完整精华实录。
引言
PostgreSQL 是全球最先进的开源关系数据库之一,其代码库庞大、功能复杂,参与社区开发常令初学者望而生畏。本文基于一场面向 PostgreSQL 潜在贡献者的技术演讲,系统梳理了从零开始参与 PostgreSQL 社区贡献的可行路径,涵盖心理准备、入门策略、补丁审查工作流,并借助一个真实案例(WAIT FOR LSN 功能的开发与审查)展示“小步启动”如何演变为对核心内幕的深入理解。
无论你是出于技术兴趣、社区情怀,还是职业发展考虑,本文都将帮助你找到第一个可落地的切入点。
1. 动机:为什么明知困难仍要参与?
每一位新人的 PostgreSQL 黑客之旅往往伴随着复杂的心情——兴奋、好奇、可能性,同时也夹杂着自我怀疑、不确定和困惑。这完全正常。从外部看,PostgreSQL 似乎已经“完成”且令人畏惧;但走近看,它依然是一个活生生的工程产品,人们在其中提问、修正补丁、改变主意。
承认这一点很重要:Hacking on Postgres 确实很难。代码库规模巨大且复杂,仅找到起点就是一道难题;审查过程严格甚至挑剔,补丁作者必须考虑众多边界情况;而数据库技术本身——优化器、事务、并发控制、复制、存储、崩溃恢复——全是硬核系统话题。
既然如此,为何仍有人乐此不疲?动机通常来自三个维度:
- 乐趣:如果你喜欢逻辑推理、用零散部件构建大型系统,PostgreSQL 内核开发本身就是一种享受。
- 社区:一次微小的改进——无论是文档修正还是测试增强——都能惠及所有用户,因为 PostgreSQL 是供应商中立的,每个功能对所有用户开放。
- 职业价值:PostgreSQL 是面向未来数据库的默认选择,覆盖开发者、企业乃至 Agentic AI 场景,参与其中能积累极具竞争力的专业资产。
这三种动机并不互斥,你可以同时享受技术挑战、帮助社区并构建职业价值。
2. 入门核心原则:从小处着手
不要试图一开始就理解全部。从足够小、能完成的任务开始,以此建立信心和熟悉度。常见的三个切入点包括:
- 审查补丁(Reviewing Patches):生成想法,了解社区思维,建立人际连接。
- 修复 Bug:小型 bug 修复通常自包含,易于追踪。
- 测试特性:通过测试了解基础功能和边界情况,无需立即设计完整功能。
这三个路径的共同点是:都给你一个具体的研究对象——某个补丁、某个 bug、某个测试失败或某种行为。
在众多起点中,演讲者最推荐的是从审查补丁开始。直接贡献新特性难度较高,而审查现有代码能让你逐步熟悉 pgsql-hackers 社区文化、隐性的编码规范以及不成文的沟通惯例。一份有用的审查不要求完美——你可以报告补丁能构建、测试某个边界情况、指出混淆的错误信息,或询问某行为是否为预期。
3. 如何正式开始审查?
3.1 订阅邮件列表
订阅 pgsql-hackers 邮件列表是进入社区的第一步,这里是官方讨论、技术决策、补丁评审的核心阵地。新手无需急于发言,优先通过长期阅读,观察社区讨论逻辑、论证方式与评审标准,逐步建立社区语境认知。
3.2 选择补丁
开发者可通过 PostgreSQL 官方 CommitFest 补丁队列挑选适配自身能力的补丁,优先选择逻辑清晰、改动范围可控的内容开展学习与评审。
4. 补丁审查的四层工作流
审查过程建议遵循由浅入深的四个层次,从基础现实检查逐步上升到设计与可维护性:
第 1 层:构建与测试
- 使用
--enable-cassert构建实例,以便捕获内存假设错误,快速失败。 - 运行
make check针对现有及新增回归测试套件。 - 报告结果时务必精确:注明分支、配置选项、测试命令;如果失败,包含失败测试名称及关键错误输出。
第 2 层:行为与可用性
- 验证补丁是否实现原始设计/规格。
- 评估 API 或命令行界面是否直观,是否过度复杂。仔细检查错误消息——PostgreSQL 应产生清晰、有帮助的错误提示,不能有静默失败或晦涩的段错误。
- 尝试无效输入、边界值和非典型操作顺序,模拟真实用户可能犯的错误。
第 3 层:性能与并发
- 性能方面:查找算法瓶颈(如 O(N²) 循环)和低效内存处理。若修改涉及性能敏感路径,使用 perf 等工具进行基准测试。
- 并发方面:审查共享状态、锁持有时间、唤醒机制和死锁风险。
- 对数据库而言,性能和并发实际上是正确性的一部分。即使无法做完整基准测试,也要询问该补丁触及的热路径是什么,以及它访问了哪些共享状态。
第 4 层:架构与文档
- 架构:该逻辑是否属于正确的层次?函数是否模块化?测试是否覆盖了新行为?
- 文档:没有文档的代码在合并那一刻就成了遗留代码。确认文档是否解释了新行为的语义,以及是否描述了用户可见的变化。
架构审查需要品味和上下文,但初学者仍可从简单问题开始:这是否重复了现有机制?测试是否描述了用户可见的行为?文档是否解释了语义?
5. 案例研究:从邮件列表到WAIT FOR LSN
5.1 问题的起源
演讲者从浏览邮件列表开始,注意到一个关于“实现等待 WAL LSN 重放”的讨论。起初这只是一个很具体的功能,却成为理解 PostgreSQL 内部机制的绝佳入口。
核心问题是在异步流复制环境下的写后读一致性。设想用户向主库写入一条评论,获得插入成功,然后立即从备库读取。由于复制是异步的,备库可能尚未重放该 WAL 记录,于是读操作可能返回空结果——从用户视角看,数据似乎丢失了。数据库自身行为正确,但用户体验却是“写入丢失”。
5.2 架构回顾
在流复制中,主库后端修改表并写入 WAL,WAL sender 将 WAL 发送给备库;备库上 WAL receiver 写入 WAL,startup 进程重放 WAL 到表中。异步场景下,后端在 WAL 本地刷盘后即可返回成功,而无需等待备库重放完成。因此存在多个位置概念:生成(generated)、发送(sent)、接收(received)、刷盘(flushed)、重放(replayed)。它们相关但保证不同。对于备库上的写后读可见性,重放是关键——仅接收或刷盘都不够,因为表数据要等到重放应用该记录后才会更新。
5.3 旧方案:轮询(Polling)
在 WAIT FOR LSN 出现之前,常见解决方案是轮询:
- 在主库使用
pg_current_wal_insert_lsn()获取目标 LSN。 - 在备库反复查询
pg_last_wal_replay_lsn(),直到追上目标。
轮询的吸引力在于实现简单,无需新 SQL 命令或新等待基础设施,只需在应用层写一个循环。但简单性的代价是反复支付,且开销发生在并非核心问题的地方。核心问题仅是比较目标 LSN 与备库重放进度,轮询却将其转化为大量 SQL 往返、连接管理和内核唤醒。
其开销有两方面:
- 每次轮询都要额外执行查询、比较 LSN,带来 SQL 执行、连接、网络成本。
- 轮询间隔与延迟存在权衡:间隔越短响应越快但系统负载越高;间隔越长负载越低但延迟增加。
常见的误解是“轮询很便宜,因为 pg_last_wal_replay_lsn() 只是简单的元数据读取”。但实际性能剖析显示,真正的 LSN 检查仅占 CPU 时间的约 1.6%,剩余开销来自 PL/pgSQL、SPI(服务器编程接口)、执行器、内核唤醒等“机械”开销。主要驱动因素包括:解释器调度开销、计划验证、SPI 结果处理、快照管理以及等待事件。轮询方案在 PG18 中的硬件指标(以 10 秒间隔测试)显示,其 cycles、instructions、cache misses 和 task-clock 均显著高于后续的等待方案。
5.4 新方案:WAIT FOR LSN 原语
WAIT FOR LSN 的流程更直接:
- 在主库更新数据。
- 调用
pg_current_wal_insert_lsn()捕获插入 LSN。 - 在备库执行
WAIT FOR LSN '<LSN>',等待该 LSN 重放完成。 - 返回成功后,应用即可安全地从备库读取,因为变更已在备库可见。
该方案避免了重复轮询带来的表达式求值开销、高频时钟检查,改为利用服务器自身的等待基础设施进行高效睡眠与唤醒。硬件对比(PG18 轮询 vs PG19 等待)显示,等待方案在 cycles(约低 10 倍)、instructions(约低 5 倍)、cache misses(约低 7 倍)和 task-clock(约低 3 倍)上均有显著改善。这种性能对比证据在审查中极具说服力,因为它将设计与测量联系起来,为其他开发者提供了可评估和挑战的具体数据。
6. 社区反馈如何塑造最终设计
技术实现只是故事的一部分。PostgreSQL 补丁通过社区反馈不断演进。
6.1 超时与错误控制
社区反馈指出命令需要 timeout 和 no_throw 选项,以便应用控制等待时长及超时行为。随后语法设计成为议题——遵循 PostgreSQL 惯例,使用WITH (utility_option_list) 形式的选项列表,确保一致性及未来可扩展性。
6.2 模式(MODE)选项的引入
最初,WAIT FOR LSN 仅支持等待“重放”模式,以实现强一致性。但社区成员指出,某些用户或集群方案可能只需要等待 WAL 被“接收”或“刷盘”即可,这些较弱的一致性保证在特定耐久性需求下仍有价值。这一反馈改变了命令的抽象边界——不再是“等待备库可见”,而是“等待 LSN 达到选定的进度状态”。
最终语法演变为:
WAIT FOR LSN '<lsn>' [ WITH ( MODE '<mode>', timeout '...', ... ) ]
支持的 mode 包括:
standby_replay(默认):等待 WAL 重放至指定 LSN。standby_write:等待 WAL 在备库上被写入(接收)。standby_flush:等待 WAL 在备库上刷盘。primary_flush:等待 WAL 在主库上刷盘。
这个设计保持了命令的通用性,避免为每个级别单独发明新命令,同时保留了原始用例(写后读一致性),并扩展了相邻场景。
7. 总结:这不是终点,而是开始
回顾这段旅程,可以提炼出几点核心经验:
- 从小处着手,但怀有宏大目标。每次审查、测试或讨论都是学习机会,知识会通过复利效应积累。
- 保持自信与坚持。补丁审查初期可能感觉笨拙,但每一次反馈都会让你的理解加深。
- 对反馈保持开放。社区审查能将粗糙的补丁打磨至可提交状态,反馈不仅修正缺陷,还会澄清目标、拓宽适用场景。
正如 Paul Graham 所说:“知识是分形扩展的,从远处看边缘光滑,但一旦学得足够多而靠近它,就会发现满是缝隙。” PostgreSQL 内核亦是如此。每次深入一个点,都会发现新的未知领域,但也正是这种发现推动着成长。
对于 PostgreSQL 黑客之路,永远没有“终点”,只有持续的开始。从一个审查、一个 bug、一个测试或一个小补丁起步,追随讨论,学习周边代码,让知识复合增长。
最重要的是——不要恐慌,从小处着手。
深耕开源生态,持续输出中国技术力量
作为深耕开源数据库国产化的标杆企业,瀚高股份长期扎根 PostgreSQL 全球开源生态,是 PG 国际社区亚太地区核心贡献者。公司累计向社区提交超150万行代码、4000余篇技术文章,完成1100余项 Patch 迭代与 Bug 修复,持续助力社区版本迭代演进。同时,瀚高发起的 IvorySQL 开源项目已捐赠至开放原子开源基金会,以长期、稳定、持续的开源投入,向全球展现中国基础软件企业的技术沉淀与生态担当。
本次香港开源之夜分享,是瀚高链接国际化开源社区、赋能开发者成长的重要实践。未来,瀚高将持续深耕数据库内核技术创新,深度参与全球开源社区共建,持续输出国产技术方案与实战经验,搭建海内外开源技术交流桥梁,助力更多开发者走进开源、深耕开源、受益开源。

222

被折叠的 条评论
为什么被折叠?



