2026 年 4 月初,开源社区爆发了一场关于 Linux 7.0 内核与 PostgreSQL 性能兼容性的激烈讨论。AWS 工程师 Salvatore Dipietro 在 Linux 内核邮件列表(LKML)上报告称,PostgreSQL 在 Linux 7.0-rc 版本上的事务吞吐量下降至原有水平的约 51%,而调度器维护者 Peter Zijlstra 拒绝回滚更改,建议 PostgreSQL 使用内核 7.0 引入的 RSEQ 扩展。这一事件不仅暴露了 Linux 调度器与高并发数据库 workloads 之间的深层矛盾,也引发了关于「不破坏用户空间」这一内核设计原则是否被打破的广泛争议。
Linux 7.0 预 emption 模型变更的技术背景
Linux 内核的预 emption 机制决定了内核在何种情况下可以强制中断正在运行的进程并将 CPU 分配给其他任务。在 Linux 7.0 之前,存在四种主要的预 emption 模型:PREEMPT_NONE 在内核代码执行期间完全不进行预 emption,适用于追求最大吞吐量的服务器场景;PREEMPT_VOLUNTARY 仅在显式的预 emption 点进行抢占,是桌面场景的折中方案;PREEMPT_LAZY 是 Linux 6.13 引入的新模型,仅在时间片边界延迟预 emption 而非立即抢占;PREEMPT_FULL 则允许随时抢占,适用于实时性要求极高的场景。
Linux 7.0 的核心变更是从主流架构(x86、ARM64、LoongArch、PowerPC、s390 等)中移除了 PREEMPT_NONE 和 PREEMPT_VOLUNTARY 选项。调度器维护者 Peter Zijlstra 将 PREEMPT_LAZY 定位为 PREEMPT_NONE 的继任者,但关键在于 PREEMPT_LAZY 名称中的「lazy」暗示了预 emption 只是被延迟,而非完全消除。这意味着即使在用户空间关键代码段执行期间,内核仍可能在时间片边界进行抢占,这一变化对依赖用户空间自旋锁的高并发应用产生了深远影响。
PostgreSQL 架构对预 emption 变更极度敏感的原因
PostgreSQL 采用多进程架构,每个客户端连接都会 fork 一个独立的 PostgreSQL 服务进程。当这些进程访问共享缓冲区池(shared_buffers)时,它们使用用户空间自旋锁(s_lock)进行互斥访问。这种设计在 PREEMPT_NONE 模型下表现优异,因为内核永远不会在进程持有自旋锁时进行抢占,关键代码段能够快速完成,其他进程在锁获取上的等待时间极短。
然而,在 PREEMPT_LAZY 模型下,内核可能在进程持有自旋锁时进行抢占。以一个 96 核服务器为例,当持有锁的进程被内核抢占时,其余 95 个进程会陷入忙等待(busy-wait),形成级联争用。性能分析显示,在新的预 emption 策略下,PostgreSQL 约 55% 的 CPU 时间消耗在用户空间自旋锁争用上,这一比例在高频事务处理场景中尤为触目惊心。
更糟糕的是,minor page fault 在这一过程中火上浇油。当新连接 fork 后首次访问共享内存时,会触发 minor page fault。在默认 4KB 页面大小下,大量 page fault 发生在持有自旋锁期间,大幅延长了锁持有时间,进一步加剧了争用问题。AWS 工程师在 m8g.24xlarge(96 vCPU Graviton4)实例上使用 pgbench 进行 benchmark 测试,在 1024 并发客户端、96 线程、持续 1200 秒的压力测试下,Linux 7.0(PREEMPT_LAZY)配置下的 TPS 约为 50,751,而恢复 PREEMPT_NONE 后的 TPS 约为 98,565,吞吐量下降幅度接近 49%。
当前可用的缓解方案与参数调优
面对这一性能回归,运维人员有几种可行的应对策略。首先,也是目前最有效的方案是启用 Huge Pages。通过将 huge_pages 参数设置为 on,可以显著减少 page fault 频率,因为 1GB 或 2MB 的大页大幅减少了页表查找和缺页中断次数。具体的配置方法是在系统中预留大页内存:echo 4096 > /proc/sys/vm/nr_hugepages(例如分配 8GB 大页),然后在 postgresql.conf 中设置 huge_pages = on。实测表明,这一配置能够大幅缓解预 emption 变更带来的性能退化。然而,在容器环境或托管数据库服务中,Huge Pages 的启用往往需要 elevated privileges,可能并不具有普适性。
其次,针对内核层面的解决方案,社区曾提交恢复 PREEMPT_NONE 的补丁,但遭到 Peter Zijlstra 的明确拒绝。他的立场是 PostgreSQL 应该使用 Linux 7.0 引入的 RSEQ(Restartable Sequences)timeslice extension。RSEQ 原本是内核为 per-CPU 数据访问提供的机制,在 Linux 7.0 中新增了 timeslice 扩展功能,允许应用在进入关键代码段前向调度器请求一段宽限期(默认 5 微秒),从而在特定场景下规避预 emption 带来的开销。
然而,PostgreSQL 社区对这一建议表达了强烈不满。 committer Andres Freund 在 LKML 上指出:用 7.0 引入的新功能来修复 7.0 引入的回归问题本身就不合理;PostgreSQL 需要支持广泛的内核版本和操作系统,RSEQ 支持仅在 Linux 7.0+ 可用;将内核级别的底层 API 集成到自旋锁实现中是一项重大设计变更,对于 PostgreSQL 14-17 等现有主流版本来说并不现实。
数据库运维人员的实际应对建议
对于正在运行 PostgreSQL 的生产环境管理者,建议采取以下分层应对策略。在评估阶段,应当使用 pgbench 或实际业务 workloads 在测试环境中复现 Linux 7.0 的性能表现,重点关注高并发写入场景下的 TPS 和延迟指标。如果观察到显著的性能下降,首选方案是启用 Huge Pages,这已在多个测试场景中验证能够将性能恢复至接近基线水平。
对于无法使用 Huge Pages 的场景,可以考虑暂时停留在 LTS 内核版本。Red Hat Enterprise Linux 和 Amazon Linux 等企业发行版会独立 backport 内核,不会立即受到这一变更的影响。Ubuntu 26.04 LTS 于 4 月 23 日发布,如果生产环境运行在 Ubuntu LTS 上,建议在补丁发布前暂停升级计划。 Fedora 和 Arch 等滚动发行版会更快收到内核 7.0,运维人员需要密切关注社区补丁动态。
从长期视角来看,PostgreSQL 开发分支已经完成了 BufFreeListLock(保护缓冲区空闲列表的自旋锁)的重构,从根本上减少了对用户空间自旋锁的依赖。这一改进将出现在未来的 PostgreSQL 主版本中,但不会 backport 到当前的 PostgreSQL 14-17 系列。对于核心业务依赖 PostgreSQL 的场景,建议与数据库团队建立密切沟通,监控内核与数据库的兼容性测试结果,并在适当时候规划版本升级。
参考资料
- Linux Kernel Mailing List (LKML) 讨论:AWS 工程师 Salvatore Dipietro 关于 PostgreSQL 在 Linux 7.0 上性能下降的报告(2026 年 4 月)
- LWN.net:Linux 3.6 调度器优化导致的 pgbench 性能回退及其回滚(2012 年)