欢迎来到我的空间

数据库不应只是操作系统上的一个进程

数据库不应只是操作系统上的一个进程

是时候重新考虑“数据库运行在操作系统之上”这个默认假设了

数据库运行在操作系统之上,这件事长期以来几乎不需要解释。操作系统负责进程、线程、文件系统、网络、内存和设备管理;数据库负责查询、事务、索引、缓存、并发控制和恢复。这个分工清晰、成熟,也支撑了过去几十年的数据库工程实践。

但现在,这个默认假设越来越值得重新审视。

原因不在于 Linux 不好,也不在于 PostgreSQL、MySQL、RocksDB 这些数据库不够优秀。真正的问题是:通用操作系统和现代数据库系统正在朝不同方向演进。操作系统越来越强调通用性、隔离性、兼容性、可维护性和公平调度;数据库则越来越强调低尾延迟、可预测执行、事务一致性、直接 I/O 路径和面向硬件的精细化控制。

二者都在进步,但优化目标并不完全一致。问题也就出在这里。

如果数据库只是一个普通应用,运行在操作系统之上当然合理。但现代数据库早已不是普通应用。它自己管理缓存,自己管理并发,自己设计日志和恢复,自己实现调度和资源控制,甚至在很多场景下还要绕过操作系统提供的默认机制。到了这个阶段,我们至少应该问一句:

继续把数据库作为普通用户态程序运行在通用操作系统之上,还是最合适的系统结构吗?

1. 数据库并不真正信任操作系统抽象

从工程实现看,数据库和操作系统之间一直存在一种微妙关系:数据库使用操作系统,但又不能完全依赖操作系统。

数据库不会简单地把内存管理交给操作系统 page cache,而是自己实现 buffer pool。数据库不会简单地依赖操作系统文件写入语义,而是自己实现 WAL、checkpoint、group commit 和崩溃恢复。数据库不会把并发控制交给操作系统锁,而是自己实现 latch、lock manager、MVCC 和事务调度。数据库也不会完全相信通用调度器能理解查询执行、锁等待、事务依赖和 I/O 完成之间的关系。

这不是数据库工程师“重复造轮子”。这是因为操作系统提供的抽象过于通用,而数据库需要的是更强的语义保证和更可预测的行为。

操作系统看到的是线程、文件、页、socket 和 block device。数据库看到的是事务、日志、脏页、索引、锁等待、查询计划和恢复点。二者面对的是同一台机器,但理解系统状态的方式并不一样。

这种差异过去也存在,只是没有现在这么尖锐。硬件慢、并发低、系统规模小的时候,多一层抽象、多几次上下文切换、多一点调度不确定性,未必会成为决定性问题。但在多核、NVMe、RDMA、云存储和低延迟服务成为常态之后,这些抽象差异会直接变成性能问题和可靠性问题。

2. Linux 调度变化影响 PostgreSQL:问题不只是一次性能回退

Phoronix 报道过一个很典型的案例:AWS 工程师在 Linux 7.0 开发周期中发现,PostgreSQL 在 Graviton4 机器上的性能出现明显下降,吞吐量大约只有此前内核版本的一半。问题与 Linux 抢占模型变化有关,PostgreSQL 在用户态 spinlock 上花费了更多时间。

参考链接:

这个案例值得关注,不只是因为“性能下降了一半”这个数字很醒目,更因为它暴露了数据库和操作系统之间的隐含契约。

PostgreSQL 的很多关键路径依赖用户态锁和短临界区。理想情况下,锁持有者很快完成工作并释放锁,其他线程只需要短暂等待。但如果操作系统在不合适的时机抢占了锁持有者,等待者就可能在用户态空转。单次等待看起来很小,放到高并发事务系统里,就可能被迅速放大成吞吐下降和尾延迟恶化。

从操作系统角度看,调整抢占模型可能有充分理由。内核要考虑的不只是 PostgreSQL,还包括大量不同类型的负载。它要维护通用性、公平性和长期架构演进。

但从数据库角度看,这种变化可能破坏多年调优出来的并发行为。数据库以为自己控制了锁和调度,实际上仍然依赖内核调度器的细节;内核并不理解这些用户态锁背后的事务语义,也不承诺永远维持某种对数据库友好的时序行为。

所以这个案例真正说明的是:

数据库的性能关键路径建立在操作系统调度语义之上,而这些语义并不是为数据库事务处理专门设计的。

这不是某个内核版本的偶然问题,而是结构性问题。只要数据库作为普通进程运行在通用 OS 上,它就必须不断适配外部调度策略的变化。

3. fsync 事件说明:文件接口和事务持久化之间存在语义裂缝

性能问题已经足够麻烦,但可靠性问题更严重。PostgreSQL 的 fsync failure 事件就是一个典型例子。

PostgreSQL 社区曾长期讨论 fsync() 失败后的语义问题。核心问题是:当 fsync() 返回错误时,数据库是否还能准确知道哪些数据已经落盘、哪些数据没有落盘、哪些错误可以恢复、哪些错误必须立即停止系统?PostgreSQL Wiki 将相关讨论整理为 “fsyncgate 2018”。后来 PostgreSQL 在 fsync 失败时采取更激进的 PANIC 策略,Linux 也在 writeback error handling 和 fsync 错误报告方面做过改进。

参考链接:

这个问题的关键不在于某个系统调用有没有 bug,而在于 fsync() 这种文件接口本来就不是围绕数据库事务语义设计的。

数据库需要知道的是:某个事务提交之后,对应的 WAL 是否已经可靠持久化?checkpoint 之前的脏页是否已经安全写入?如果写入失败,失败范围是什么?恢复协议应该从哪里开始?系统是否还能继续接受新事务?

操作系统提供的却是另一套抽象:文件、页缓存、writeback、块设备、设备缓存和错误码。数据库把事务语义压在这些文件接口之上,中间隔着多层缓冲、重排和错误传播路径。大多数时候它能工作,但一旦遇到异常路径,语义边界就会变得非常复杂。

这说明数据库和操作系统之间并不是简单的“调用接口”关系。数据库需要的是事务级持久化语义,操作系统提供的是文件级 I/O 语义。二者之间靠约定、经验、补丁和大量工程细节维持一致。

对于一个把“数据不能丢”作为基本要求的系统来说,这种边界并不理想。

4. DBOS 的启示:数据库不再只是应用层组件

DBOS 的观点提供了另一个角度。DBOS 不是简单讨论数据库性能优化,而是提出一个更激进的问题:既然数据库擅长管理状态、一致性、恢复和查询,为什么操作系统状态不能用数据库方式来管理?

参考链接:

DBOS 的基本思路是把操作系统状态表示为数据库表,让原本分散在内核和运行时中的状态变得可查询、可恢复、可调试、可迁移。这背后有一个很重要的判断:现代系统真正困难的部分,越来越集中在状态管理上。而数据库恰恰是人类工程体系中最成熟的状态管理系统之一。

当然,DBOS 的方向和“为数据库重新设计运行时”并不完全相同。DBOS 更像是把操作系统数据库化;而这里讨论的问题,是数据库是否应该继续依赖传统通用 OS 的抽象。

但二者有一个共同点:它们都不再把数据库看作普通应用。数据库开始从应用层组件,变成系统结构的中心之一。

这其实是一个很重要的趋势。过去我们问的是:“数据库如何更好地运行在操作系统上?”现在也许应该反过来问:“操作系统能力应该如何围绕数据库这样的状态系统重新组织?”

5. SPDK 和 DPDK:高性能路径已经在绕过内核

如果通用操作系统抽象足够适合高性能数据系统,SPDK 和 DPDK 就不会这么重要。

SPDK 的思路很直接:把 NVMe 驱动等关键路径放到用户态,通过轮询、异步、零拷贝和无锁消息传递减少 syscall、中断和内核路径开销。

DPDK 也是类似逻辑:通过用户态 Poll Mode Driver 和 kernel bypass,让网络包处理避开传统内核网络栈,从而获得更高吞吐和更低延迟。

参考链接:

这些技术的存在说明,高性能系统已经在用脚投票。对于关键数据路径,通用内核机制经常不是最优选择。中断、syscall、内核调度、通用协议栈和锁竞争带来的开销,在普通应用里可以接受,但在数据库、存储系统、网络系统和交易系统里会变得非常明显。

数据库和 SPDK/DPDK 的需求高度相似:低延迟、低抖动、高吞吐、可控调度、可预测资源使用。既然存储和网络领域都已经走向用户态驱动和 kernel bypass,那么数据库是否也应该重新思考自己的运行时边界?

这并不是说所有数据库都应该绕过内核。问题在于,关键路径是否还应该默认依赖通用内核机制。如果数据库最重要的 I/O、调度和持久化路径都需要额外机制来绕开 OS,那么“数据库只是 OS 上的普通进程”这个抽象就已经开始失效了。

6. Unikernel 的启示:数据库不需要完整的通用 OS

Unikernel 的思路是把应用和它需要的最小操作系统库编译成一个专用镜像。它不是运行在完整通用操作系统之上的普通应用,而是一个为特定应用裁剪出来的系统环境。

参考链接:

这个思路对数据库很有启发。

数据库通常不需要图形界面,不需要桌面子系统,不需要大量通用设备驱动,不需要完整用户管理模型,也不需要绝大多数通用 syscall。数据库真正需要的是可控 CPU 调度、高性能内存管理、明确持久化语义、高效网络 I/O、崩溃恢复、故障隔离和面向数据访问的安全模型。

也就是说,数据库需要的不是“更多 OS 功能”,而是“更符合数据库语义的少量系统功能”。

现代通用操作系统非常庞大。庞大带来的不仅是功能丰富,也包括更大的攻击面、更复杂的失败模式和更难预测的交互路径。对于数据库这种状态密集、性能敏感、可靠性要求极高的软件来说,完整 OS 可能不是天然合适的运行环境。

这并不意味着数据库一定要做成 unikernel,也不意味着要放弃 Linux 生态。更实际的结论是:数据库需要一个比普通进程更贴近硬件、比完整 OS 更贴近数据语义的运行层。这个运行层可以建立在 Linux 之上,也可以通过 library OS、unikernel、microkernel、虚拟化或用户态驱动实现。

重点不是选择哪一种标签,而是重新划定边界。

7. 真正的问题是语义不匹配

讨论到这里,问题已经不是“Linux 是否足够好”。Linux 当然足够好,而且在很多方面几乎不可替代。它的硬件支持、生态、工具链和运维体系,是任何新系统都很难绕开的。

但 Linux 是通用操作系统,不是数据库执行内核。它必须服务各种负载:桌面应用、容器、编译任务、Web 服务、HPC、嵌入式设备、网络设备、文件服务器和数据库。它的目标是通用性,而不是为数据库事务处理提供最优语义。

数据库的问题则非常具体:事务如何调度,锁等待如何处理,WAL 何时持久化,脏页如何刷盘,I/O 错误如何传播,查询任务如何和网络事件协同,CPU cache 如何减少抖动,尾延迟如何控制。

因此,OS 和 DB 的错位主要体现在三个方面。

第一,调度语义不匹配。操作系统调度线程,数据库调度事务和查询。OS 看见的是 runnable task,DB 看见的是锁依赖、事务冲突、查询阶段和 I/O 等待。

第二,I/O 语义不匹配。操作系统处理文件、页缓存和块设备,数据库处理 WAL、checkpoint、dirty page 和恢复点。

第三,安全语义不匹配。操作系统围绕进程、用户、文件权限和 syscall 做隔离,数据库围绕库、表、行、列、事务、角色、审计和访问路径做隔离。

这些都不是通过增加几个 syscall 就能彻底解决的问题。它们说明 OS 和 DB 对系统状态的建模方式不同。

8. 可能的方向:把数据库运行时提升为系统边界

传统结构是这样的:

应用服务
数据库客户端 / RPC / SQL
数据库进程
操作系统 syscall / 文件系统 / 网络栈 / 调度器
硬件

这个结构默认认为数据库是操作系统上的一个复杂应用。

未来更值得探索的结构可能是:

应用逻辑 / 查询 / 事务
数据库运行时
面向数据库语义的调度、I/O、持久化、安全和恢复机制
最小 OS / 专用内核 / 用户态驱动 / 虚拟化层 / 硬件

在这种结构里,数据库不只是调用 OS 机制,而是把一部分原本交给 OS 的关键能力收回到数据库运行时中。这个运行时理解事务、查询、锁等待、日志、恢复和数据访问路径,因此也更有机会做出符合数据库目标的调度和资源控制。

这条路线可以是渐进式的,不必一步到位。比如:

  1. 在 Linux 之上构建数据库专用 runtime,减少对通用 OS 语义的依赖;
  2. 对关键存储路径使用 SPDK、io_uring 或 direct I/O;
  3. 对关键网络路径使用 DPDK、io_uring 或更轻量的用户态网络机制;
  4. 将查询、事务、I/O 完成事件和应用过程调用纳入统一调度;
  5. 为数据库设计更明确的持久化接口,而不是只依赖传统 fsync() 语义;
  6. 把应用逻辑部署到数据库运行时内部,减少跨进程、跨网络、跨服务调用;
  7. 探索 library OS、unikernel、microkernel 或数据库专用 kernel 的形态。

这里的核心不是“抛弃 Linux”,而是减少数据库关键路径对通用 OS 语义的依赖。Linux 仍然可以承担启动、隔离、部署、观测和硬件生态等基础角色。但数据库最核心的执行、调度和持久化语义,应该逐渐回到数据库运行时自身。

9. 数据库的未来可能不是更好地适配 OS,而是重新定义 OS/DB 边界

过去很多数据库优化都围绕“如何更好地使用操作系统”展开:更好地使用文件系统,更好地使用 page cache,更好地使用线程,更好地使用网络栈。

这条路线仍然有价值,但它可能不是全部未来。

当数据库已经自己管理缓存、并发、事务、日志、恢复和调度时,它事实上已经在实现一套面向数据的系统运行时。既然如此,继续把它放在一个并不理解事务语义的通用 OS 抽象之上,就会越来越别扭。

Linux 调度变化影响 PostgreSQL,说明数据库性能依赖 OS 调度细节。fsync failure 说明文件系统接口和事务持久化之间存在语义裂缝。SPDK 和 DPDK 说明高性能数据路径正在绕过内核。DBOS 说明数据库思想正在反向影响操作系统设计。Unikernel 说明专用系统环境有机会缩小复杂性和攻击面。

这些现象放在一起,指向一个判断:

数据库和操作系统的传统边界,需要重新设计。

未来的数据密集型系统,可能不再是“数据库运行在操作系统上”这么简单。更合理的方向,可能是以数据库为中心重新组织执行、调度、持久化、网络、安全和部署能力。

数据库不一定要取代操作系统,但数据库应该拥有更强的系统边界。对于高并发、低延迟、强一致、状态密集的应用来说,数据库也许不应再只是一个被操作系统调度的进程,而应该成为管理数据服务应用执行的平台。

这也是下一代数据库系统真正值得探索的方向。

数据库不应只是操作系统上的一个进程,也不应只是应用服务背后的存储组件。它应该进一步成为数据服务应用的执行平台。

MuduDB:探索数据库与操作系统的新边界

MuduDB[https://github.com/scuptio/mududb] 是一个面向数据服务应用的新一代数据管理系统。它的目标不是做一个更快的存储引擎,或另一个 SQL 数据库,而是重新组织应用逻辑、事务处理、状态访问与数据库运行时之间的关系。

MuduDB 希望让一部分数据密集型应用逻辑直接进入数据库运行时,使计算更靠近数据,让事务、状态访问和业务过程在同一个执行环境中协同完成。

因此,MuduDB 更接近一个“统一执行与数据平台”:它不仅提供存储、查询和事务能力,也尝试承载应用过程、运行时调度、状态管理与部署分发等能力。

MuduDB 未来的路线,不只关注数据库与应用之间的边界,也会进一步重新思考数据库与操作系统之间的边界。它要探索的不是“接管整个操作系统”,而是工作在那些通用操作系统并不真正理解、但对数据库场景至关重要的部分:事务语义、调度、数据访问路径、持久化边界。


参考资料

  1. Phoronix, Linux 7.0 On AWS Graviton4 Seeing PostgreSQL Performance Drop To Half
    https://www.phoronix.com/news/Linux-7.0-AWS-PostgreSQL-Drop

  2. PostgreSQL Wiki, Fsync Errors
    https://wiki.postgresql.org/wiki/Fsync_Errors

  3. Zongzhi Chen, Starting from PostgreSQL’s fsync Failure
    https://medium.com/@baotiao/starting-from-postgresqls-fsync-failure-840af156585c

  4. Matei Zaharia et al., DBOS: A Proposal for a Data-Centric Operating System
    https://arxiv.org/abs/2007.11112

  5. DBOS Project
    https://dbos-project.github.io/

  6. SPDK, About SPDK
    https://spdk.io/doc/about.html

  7. DPDK Project
    https://www.dpdk.org/

  8. NVIDIA, DPDK
    https://developer.nvidia.com/networking/dpdk

  9. Anil Madhavapeddy et al., Unikernels: Library Operating Systems for the Cloud
    https://dl.acm.org/doi/10.1145/2490301.2451167

  10. Simon Kuenzer et al., A Security Perspective on Unikernels
    https://arxiv.org/abs/1911.06260