“Serverless 数据库”这个词被云厂商滥用得很厉害。市场宣传里,它指”按量计费、无需运维”;但作为数据库工程师真正关心的问题是:当流量从零涨到每秒几千请求时,数据库能不能在几十毫秒内加出容量?当流量回落时,能不能回到近零成本?在伸缩过程中,事务一致性、连接状态、缓存预热怎么处理?
这些问题的答案,取决于底层存储架构。如果数据库还是经典的”计算与存储绑在同一台机器上”的单体,弹性就只能做到”机器级别”,伸缩粒度粗、冷启动慢、分支克隆要全量拷贝。要想做到真正的按秒计费、按请求数秒级扩容、零数据拷贝的分支,必须把存储从计算里拆出来,让计算层无状态、可随时启停。
AWS Aurora(SIGMOD 2017、2018)开创了”日志即数据库(Log is Database)“的架构,把 redo 日志作为计算和存储之间的唯一接口;Aurora Serverless v2 在此基础上把计算层做成 ACU(Aurora Capacity Unit)为粒度的细粒度弹性资源池;开源项目 Neon(Khlystov 等人 arXiv 2023)则更进一步,把存储拆成 safekeeper(日志持久化)和 pageserver(页面物化)两个独立层,让计算节点完全无本地状态,可以在秒级启停,也可以在毫秒内做 copy-on-write 分支。
本文上半部分拆解 Aurora 与 Neon 的架构差异、冷启动延迟的根源、分支/克隆的实现。下半部分给出一个本地可跑的 Neon docker-compose demo,并把 Serverless 数据库放回到本仓库的”存算分离”系列里看。
版本说明 本文引用 Aurora(至 2024 年公开资料)、Aurora Serverless v2(发布于 2022)、Neon 公开代码仓库(
github.com/neondatabase/neon)与 arXiv 论文。
一、为什么 Serverless 数据库一定要存算分离
1.1 单体数据库的弹性天花板
一个经典的单体 PostgreSQL 实例,把数据文件、WAL、shared buffers、连接状态全部放在同一台机器上。要给它做弹性,只有两条路:
- 垂直伸缩:把机器换成更大规格。代价是要重启或 failover,通常秒到分钟级中断;更大规格的机器也不一定现货。
- 水平伸缩:加只读副本。代价是主库写入能力没涨,分片还要应用层感知。
“按秒计费、按流量自动扩缩”这种承诺,在这种架构下根本实现不了。问题核心是:计算节点持有本地状态——数据文件、WAL、buffer pool。只要这些状态绑定在某台机器上,这台机器就不能随便启停。
1.2 Aurora 的关键一跳:日志即数据库
Verbitski 等人在 SIGMOD 2017 的论文 “Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databases” 给出一个关键观察:
“The log is the database.”
PostgreSQL / MySQL 的崩溃恢复原理是”从一致检查点开始重放 WAL”。既然 WAL 足够重建任何页面,那么把”页面”本身从计算节点里剥掉,让它由存储层根据 WAL 自己物化出来,就完全可行。Aurora 的做法:
- 计算节点只写 WAL(redo log),不再直接写数据页;
- WAL 被发送到由 6 个副本组成的存储层(跨 3 AZ,每 AZ 两个副本);
- 存储层各自重放 WAL,在本地物化数据页;
- 读取时,计算节点从存储层请求某个页的某个 LSN 版本。
这带来三个直接收益:
- 写入路径只发 redo,而非整页,网络流量大幅减少(论文里数字约是 1/7.7);
- 计算节点不再有”脏页”需要刷盘,故障恢复时不需要走 PostgreSQL 的 checkpoint/redo 机制,直接让存储层接管;
- 计算节点几乎无状态,重启只需要连上存储层。
这是 Aurora Serverless 能存在的前提。Aurora 先有了”存算分离”的主干架构,再把计算层包装成可弹性的 ACU。
从另一个角度看,这个设计也把数据库”崩溃恢复”与”副本同步”统一成同一件事:副本不再靠”追 binlog / streaming replication” 保持状态,而是所有副本都对同一份 WAL 做独立回放;主库崩溃时,任何副本的存储层都已经把 page 物化好了,新主只要接上存储层就立刻可用。这让 Aurora 的故障恢复时间从传统 PG 的分钟级降到秒级。
1.3 Aurora Serverless v2 的 ACU 与细粒度伸缩
Aurora Serverless v1(2018)用的是”机器级”伸缩——流量高就换大机器、低就换小机器,切换时有几十秒到分钟级的中断。v2(2022)重新设计了资源模型:
- ACU(Aurora Capacity Unit):包含约 2 GiB 内存、对应 CPU 与 I/O 份额的抽象单位;
- 数据库实例在预设的 min/max ACU 之间原地伸缩,不切换物理机;
- 伸缩步长到 0.5 ACU,伸缩周期可以到亚秒级;
- 计算节点预留了资源池,伸缩本质上是调度器调整 cgroup / 虚拟化资源配额。
v2 的弹性不再需要停库切实例,因此冷启动与连接保持都大大改善。但”min ACU”仍然大于 0——Aurora Serverless v2 的最低配置是 0.5 ACU,不能真正”无流量时缩到零”(最新版本引入了 0 ACU 自动暂停能力,恢复时需要重新唤醒,有冷启动延迟)。
1.4 和 Aurora 的关系
Neon 团队多次在博客里明确说:Neon 是 Aurora 思路的开源重实现,但做了一个关键的拆分——把存储层再切成”持久化日志”和”页面物化”两个独立服务。如果说 Aurora 把”日志即数据库”的思想第一次工程化,Neon 则把这个思想推进到:
- 日志持久化的粒度、共识机制、副本数,应该与页面物化解耦;
- 页面物化可以完全构建在对象存储之上,获得廉价的长期存储;
- 计算层的 Postgres 代码改动越少越好,以便追踪上游。
这三条决定了 Neon 的架构边界。下面具体拆开看。
二、Neon 的三层架构:compute / safekeeper / pageserver
2.1 结构图
Neon 把 PostgreSQL 的存储层进一步拆成两个独立组件:
+---------------------+
SQL client ---> | compute node | 无状态 Postgres 进程
| (stateless PG) |
+----------+----------+
| WAL (libpq over TCP)
v
+---------------------+
| safekeeper tier | 3 副本,Paxos/Raft 风格共识
| (durable WAL) | 保证 WAL 持久化
+----------+----------+
| WAL stream
v
+---------------------+
| pageserver tier | 物化页面、缓存、分支
| (page store) |
+----------+----------+
| 对象存储(S3)
v
+---------------------+
| object storage | 长期持久化
+---------------------+
每一层都可独立伸缩:
- compute:从 Patroni / PostgreSQL 源码修改而来,去除了本地持久化。启动时连接 pageserver 拉页,写入时把 WAL 发到 safekeeper。
- safekeeper:专门持久化 WAL 的服务,跑 Paxos 族共识(Neon 内部文档称 “Paxos-like”)。它的唯一职责是保证”commit 过的 WAL 不丢”。
- pageserver:消费 WAL,按页面物化成 Neon 自定义的分层存储(delta layer + image layer,类似 LSM)。还负责响应计算节点的 getpage@LSN 请求,以及管理分支。
这个拆分的意图:让”持久化共识”和”页面物化”成为两件独立的事。Aurora 把它们合在一个存储节点里,Neon 则分开,代价是多一次网络跳转,收益是两层可以分别针对”低延迟写”和”高吞吐读”做优化。
2.2 safekeeper:把 WAL 写穿到共识层
safekeeper 不是普通的复制组件,它是 Neon 的”数据库真相”。事务提交必须等 safekeeper 多数派(3 选 2)确认 WAL 落盘。safekeeper 之间通过共识协议选主、同步日志。
这里的共识协议与经典 Raft(见《Raft 深度解析》)有细微差异:Neon 不需要在 WAL 上做”状态机应用”,safekeeper 的作用纯粹是持久化,状态机执行是下游 pageserver 的事情。因此 safekeeper 可以省掉经典 Raft 中的 apply loop,只保留 leader election + log replication。
2.3 pageserver:把 WAL 变成可随机访问的页面
pageserver 从 safekeeper 拉 WAL stream,针对每个 Postgres 关系(表/索引)维护一个分层结构:
- Delta Layer:记录某个 LSN 区间内、某组页面的增量(WAL record);
- Image Layer:某个 LSN 点上某组页面的完整映像;
- 计算页面
get_page(rel, blkno, lsn)时,从下往上找:最新的 image layer 之上的所有 delta layer 叠加起来,就是该 LSN 下的页面内容; - 后台做 compaction,把积累的 delta layer 合并成新的 image layer,避免重放链过长。
这个结构和 LSM-Tree 思想相通,只是叶子单位是”Postgres 数据页”而非 KV。详细机制在本仓库《LSM-Tree 工程实践》里有类比。
pageserver 还把冷数据下沉到对象存储(S3)。热数据在本地 SSD 上,冷数据按 layer 文件粒度上传 S3,访问时按需拉回。这让存储成本可以做到接近对象存储的水平。
2.4 compute:彻底无状态的 Postgres
Neon 的 compute 节点是修改过的 Postgres,关键改动:
- 替换 smgr(Storage Manager):Postgres
的
md.c是默认的本地文件 smgr,Neon 替换成neonsmgr,mdread/mdwrite变成 “从 pageserver 拉 / 把 WAL 发到 safekeeper”。 - 去掉 checkpoint:compute 节点不再做周期性 checkpoint,也没有 shared_buffers 刷盘路径。所有”脏”实际上是 WAL,发到 safekeeper 后就算持久化。
- 启动时不扫 data directory:传统 Postgres 启动要扫 data dir 恢复,Neon 的 compute 启动时 data dir 是空的,只要连上 pageserver 就能按需取页。
这些改动让 compute 节点的冷启动从”分钟级 PG 恢复 + 全量数据盘拷贝”变成”几秒内新起 Postgres 进程 + 连接 pageserver”。
2.5 为什么要再引入 storage_broker
Neon 架构图里还有一个小组件
storage_broker,它在 compute / safekeeper /
pageserver 之间做元数据 pub/sub:
- safekeeper 定期把”我已经持久化到哪个 LSN”上报 broker;
- pageserver 从 broker 订阅 safekeeper 的位点,决定从哪个 safekeeper 拉 WAL;
- compute 通过 broker 查找它所属 tenant 的 safekeeper 和 pageserver 地址。
这让控制面和数据面解耦:数据面走 libpq / TCP 直连(低延迟),控制面走 gRPC 到 broker(发现 + 协调)。类似的设计在很多”分层服务”系统里都能看到,它的价值是让每层都能独立重启而不丢全局视图。
三、冷启动延迟的根源
3.1 Serverless 数据库的冷启动分解
Serverless 数据库的”冷启动”指:从 0 流量状态被唤醒到第一个查询完成的端到端延迟。它可以拆成:
cold_start = t_schedule + t_provision + t_pg_start + t_auth + t_prewarm + t_first_query
t_schedule:调度器决定在哪个物理节点拉起 compute;t_provision:分配资源、准备网络、挂载卷(如果有);t_pg_start:启动 Postgres 进程(含 fork 各种 worker);t_auth:TLS 握手 + 认证;t_prewarm:把常用页面预热到 shared_buffers;t_first_query:执行第一个查询本身。
对 Neon,因为 compute
完全无状态,t_provision
基本消失;t_pg_start
在几百毫秒量级;t_prewarm
可以懒加载(查询时按需拉页)或借助 “last known working
set”(把上次 shutdown 时的 buffer
映射保存为元数据)。端到端冷启动,Neon
公开文档里给出的数字大致是 “次秒到几秒”。
Aurora Serverless v2 在 min ACU > 0 时没有冷启动(实例一直在跑),只有”扩容延迟”;在缩到 0 ACU 的场景下,冷启动由 AWS 内部资源池决定,公开数字在秒级。
3.2 冷启动与 connection pooling
即使数据库本身冷启动做到了亚秒级,应用侧每个请求新建
TCP/TLS + Postgres 认证也要 50–200 ms。因此 Serverless
数据库通常配套一个连接池代理(Neon 用 pgbouncer
类组件,Aurora 用 RDS
Proxy),让应用的连接打到代理、代理再用长连接池连到
compute。冷启动期间代理可以 buffer 请求,等 compute ready
再转发。
3.3 冷启动 vs 持久化语义
有一个容易踩的坑:冷启动期间,事务的 durability 语义不能弱化。 即便 compute 是新起的、pageserver 是新加载的,只要应用看到了”commit 成功”,就必须保证 safekeeper 已经多数派落盘。任何把冷启动时的写入做成”异步刷日志”的优化都会破坏 ACID。
这也是 Neon 把 safekeeper 单独列为一层的原因之一:safekeeper 自己维护共识,不会因为 compute 或 pageserver 冷启动而丢数据。
3.4 实测冷启动的方法
要诊断一款 Serverless 数据库的冷启动到底卡在哪,最实用的办法是分段打点。下面是一段最小的 Python 探针,用来比较 “完全冷启动” vs “热启动” 的延迟分布:
# post/db-frontier/13-serverless/demo/cold_start_probe.py
import os, time, statistics, psycopg
DSN = os.environ["NEON_DSN"]
def one_round(label):
t0 = time.perf_counter()
with psycopg.connect(DSN, connect_timeout=30) as conn:
t1 = time.perf_counter()
with conn.cursor() as cur:
cur.execute("select 1")
cur.fetchone()
t2 = time.perf_counter()
return {
"label": label,
"connect_ms": (t1 - t0) * 1000,
"first_query_ms": (t2 - t1) * 1000,
"total_ms": (t2 - t0) * 1000,
}
samples = []
for i in range(10):
# 触发"冷启动"的方式因产品而异:Neon 上可以先等若干分钟无流量,
# 或者调用 suspend API。
samples.append(one_round(f"round-{i}"))
time.sleep(5 * 60)
for k in ["connect_ms", "first_query_ms", "total_ms"]:
vals = [s[k] for s in samples]
print(k, "p50", statistics.median(vals), "p95", sorted(vals)[int(0.95*len(vals))])这段脚本不追求完美,它存在主要是把”冷启动”从营销口号变成可比较的数字。实际测 Neon / Aurora Serverless v2 / 其他产品时,只要 DSN 和 suspend 触发方式不同,主体可以复用。
3.5 冷启动与 buffer pool 预热的权衡
“冷启动完成”只是表面——真正对业务体感负责的是buffer pool 是否预热。空 buffer pool 下,每次查询 page miss 都要走远端,端到端延迟常在几十毫秒到几百毫秒。两种常见策略:
- eager prewarm:启动时预读一批历史热 page。优点是命中率起步高;缺点是启动耗时拉长,且预测不准时浪费带宽。
- lazy warm:按查询需求即时拉 page。优点是启动快;缺点是 p99 在预热期偏高。
Neon 采取的是一个折中:compute shutdown 前把当前 buffer pool 的页号列表持久化到元数据,下次启动时优先”热身”这些页。代价是一次额外的元数据写,收益是冷启动后命中率能快速恢复到 shutdown 前的水平。
这个策略对”短暂缩容后又扩容”场景尤其重要:如果没有保存 working set,每次缩到 0 再扩容,都是完全空的 buffer pool,业务体感相当差。
3.7 冷启动不只是性能问题,也是安全与可观测性问题
冷启动阶段的数据库处于”刚起来、状态还不完整”的窗口,有几个容易被忽视的风险:
- 监控指标缺失:刚启动时 metrics exporter 还没 ready,观测侧看到一段”黑屏”;
- TLS 证书/密钥加载延迟:涉及 KMS 或云侧密钥服务时,冷启动会多出几百毫秒到几秒;
- 权限/IAM 的首次调用:IAM token 拉取、审计日志建立连接这些通常只在第一次执行时发生,实测偶尔能看到秒级抖动;
- 连接池代理的会话状态:冷启动后所有历史会话失效,应用侧要有重连与重放机制。
这些问题不解决,即使冷启动延迟做到亚秒级,上线后依然会被”第一批请求失败” 的反馈轰炸。因此 Serverless 数据库的冷启动工程,技术指标之外还要配套健壮的”热身预检 + 健康探针”,确保外界感知到的是”已经完整可用的数据库”而不是”刚起来的半成品”。
四、分支与克隆:copy-on-write 的价值
4.1 为什么分支在数据库里这么贵
传统数据库”克隆一个库”的典型做法是 pg_dump + pg_restore 或文件级快照。对 100 GB 的库,分钟到小时级;对 1 TB 以上,通常只能走存储层的快照(如 EBS snapshot),还要走相对昂贵的 API。
但”分支(Branch)“是开发流程里的刚需:
- 每条 feature 分支想有独立的数据库;
- 做 migration 测试要一个 1:1 的副本;
- 跑重型数据分析不想影响生产。
如果克隆要小时级,这些场景只能退化成”用小数据集”或”在生产库上小心翼翼”。
4.2 Neon 的 copy-on-write 分支
Neon 的分支本质上是pageserver 的元数据操作,不复制任何数据:
- 在某个 LSN L 上,把现有 timeline 标记为 “parent”,创建新 timeline “branch-X”,parent_lsn=L。
- branch-X 的 WAL 从新的 compute 节点写入,自成序列。
- 读取时,对于 LSN ≥ L 的页面,从 branch-X 自己的 delta/image layer 找;找不到就回溯到 parent timeline 的 layer。
- 写入时,在 branch-X 的 delta layer 上 append WAL。
关键观察:branch 创建是 O(1) 的元数据操作,不论库大小。一个 1 TB 的数据库,开一个分支在毫秒级完成。只有分支自己的写入会产生新 layer;读到的 parent layer 是共享的。
这和文件系统的 copy-on-write(Btrfs / ZFS,见本仓库《Btrfs CoW》)思路一致,只是粒度从 “文件/块” 换成了 “LSN 范围内的页面”。
4.3 Aurora 的 clone
Aurora 也提供数据库克隆能力,机制类似:存储层在 snapshot LSN 上给 clone 打元数据,原始卷和 clone 卷共享底层 chunk,只有写入的 chunk 才 copy-on-write。本质上和 Neon 的 branch 属于同一类,区别在于 Neon 的粒度(layer 文件)更细、对象存储友好度更高。
4.4 分支在工作流里的用法
一个实际的 dev loop:
# 假设使用 neonctl
neonctl branches create --name feat-ABC --parent main
neonctl connection-string feat-ABC
# 在 feat-ABC 上跑 migration、跑测试
# 测完删除
neonctl branches delete feat-ABC整个过程不拷贝任何数据。对 CI 尤其有用:每个 PR 开一个数据库分支,跑完销毁,零存储成本(除了该分支产生的新页面)。
4.5 PITR(Point-in-Time Recovery)
branch 的近亲是 PITR——回到过去某个时间点的数据库状态。两者的区别只是”是否保留原分支”:
- branch:在历史点 L 上分叉出一个新 timeline,原 main 继续前进;
- PITR:把 main 直接回退到历史点 L(实践上通常是”在 L 点开一个 branch 并把它 promote 成 main”,避免破坏性操作)。
Neon 与 Aurora 都是按 LSN 或时间戳索引 WAL 和 image,PITR 的代价和精度都比传统快照 + binlog 方案好得多:
- 精度:LSN 级,理论上到每个 commit;
- 代价:O(WAL_size + 必要 image),不需要完整全量拷贝;
- 窗口:取决于 WAL / image 保留策略,典型 7–30 天。
对”误删一张表”这类事故,PITR 几分钟内能把数据恢复到事故前,比从备份恢复快一个数量级。
五、本地可跑的 Neon demo
5.1 demo 目标
在本机启动一套 Neon:1 个 pageserver、1 个 safekeeper(单节点,开发模式)、1 个 compute。用 psql 建库、写数据、做一次分支,观察 WAL 与 layer 文件的变化。
完整脚本放在 demo/,下面把关键步骤列出。
5.2 docker-compose.yml 思路
Neon 官方仓库提供了 docker-compose
配置,但分布在多个文件里。本地 demo 可以精简成一个
compose:
# post/db-frontier/13-serverless/demo/docker-compose.yml
services:
storage-broker:
image: neondatabase/neon:latest
entrypoint: ["/usr/local/bin/storage_broker", "--listen-addr=0.0.0.0:50051"]
ports: ["50051:50051"]
pageserver:
image: neondatabase/neon:latest
depends_on: [storage-broker, safekeeper]
environment:
- BROKER_ENDPOINT=http://storage-broker:50051
entrypoint: >
/usr/local/bin/pageserver
-D /data/.neon
-c "broker_endpoint='http://storage-broker:50051'"
-c "listen_pg_addr='0.0.0.0:6400'"
-c "listen_http_addr='0.0.0.0:9898'"
ports: ["6400:6400", "9898:9898"]
volumes: ["pageserver-data:/data"]
safekeeper:
image: neondatabase/neon:latest
depends_on: [storage-broker]
entrypoint: >
/usr/local/bin/safekeeper
--listen-pg=0.0.0.0:5454
--listen-http=0.0.0.0:7676
--id=1
--broker-endpoint=http://storage-broker:50051
-D /data
ports: ["5454:5454", "7676:7676"]
volumes: ["safekeeper-data:/data"]
compute:
image: neondatabase/compute-node-v16:latest
depends_on: [pageserver, safekeeper]
environment:
- PG_VERSION=16
ports: ["55432:55432"]
# 具体参数依官方镜像 entrypoint 设计,demo/README.md 里有完整命令
volumes:
pageserver-data:
safekeeper-data:注意:Neon
官方镜像随版本改动较大,上面是思路示意。demo/README.md
里会锁定一个具体 tag(例如
release-8123),并给出该 tag
下验证过的完整命令,保证离线可跑。
5.3 实验步骤
docker compose up -d,等容器就绪。psql postgresql://cloud_admin@localhost:55432/postgres,建表写几百万行。curl localhost:9898/v1/tenant/<tid>/timeline查看 timeline 与 layer 文件。- 在 pageserver 的 HTTP API 上触发一次
checkpoint、compact,观察 delta layer 合并成 image layer。 - 调用 pageserver API 创建 branch,在新 timeline 的 compute 上建连接,验证父 timeline 的数据可见但互不影响。
- 停掉
compute,再重启,测冷启动时间(
time psql -c 'select 1')。
这个 demo 不追求性能数字,它要让读者在本机亲眼看到:branch 真的是 O(1) 元数据操作;compute 重启真的不用拷贝任何页面;WAL 和 page store 真的分离。
5.4 demo 的局限
- 单节点 safekeeper 不具备生产意义的高可用;
- pageserver 单实例,没有跨 AZ 的容灾;
- 对象存储用本地文件系统模拟,真实部署应接 S3/GCS/MinIO;
- 性能远低于生产——所有组件跑在一个 Docker host 上,网络与磁盘竞争严重。
生产级部署参见 Neon 官方 operator / k8s 部署文档。
5.5 从 demo 延伸到生产理解的几个问题
在 demo 跑通后,建议读者自己用 API 探索以下几个问题,把”看到架构”变成”理解架构”:
- 写入 1 万行 + 立即 branch 后看 layer 文件:新 branch 的 layer 目录里应该几乎是空的,只有元数据。对照主 timeline 的 layer 目录,能直观看到 copy-on-write 的”零复制”。
- 持续写入,观察 delta -> image compaction
过程:用 pageserver HTTP API
/v1/tenant/.../timeline/.../compact手动触发,对比 compaction 前后 layer 文件数量与 total size。 - 杀掉 compute,立即重启,测从连接到第一条查询的耗时:用第三节的 probe 脚本。比较”连了一次之后的热启动”与”容器重启的冷启动”。
- 把 safekeeper 暂停 5 秒再恢复:观察 compute 侧写入是否会阻塞、阻塞多久、恢复后是否继续。
- 在 compute 上跑一次 pg_basebackup 会失败:因为 compute 本地没有完整 data directory。这个”失败”本身说明了架构与传统 PG 的区别。
这些小实验对理解 “无状态 compute”、“分层存储”、“branch 的本质” 比读多少架构文档都有用。
六、与本仓库”存算分离”系列的关系
本仓库其他几篇文章构成一条”存算分离”主线:
- 《共享存储架构》 讲 shared-disk 的共识模型;
- 《Disaggregated DB 合集》(下一篇)系统对比 Aurora / Socrates / PolarDB / Taurus;
- 《LSM-Tree 工程实践》 给出 layered storage 的调优思路,可以类比 pageserver 的 delta/image layer。
Neon 的架构可以被视为”Aurora 思想在开源世界的细化”:safekeeper 把 Aurora 存储层的 WAL 持久化职责拆出来、pageserver 把页面物化职责拆出来。理解了 Aurora 再看 Neon,会觉得每一层拆分都很自然;反过来只看 Neon 可能会觉得”为什么搞得这么复杂”。
6.4 与 storage 系列的互链
Serverless 数据库的每一层,本仓库 storage 系列都有对应的深入文章:
- pageserver 的分层存储思路对应 《LSM-Tree 工程实践》;
- 冷数据下沉到对象存储对应 《存储介质选型》;
- copy-on-write 的文件系统视角对应 《Btrfs CoW》 与 《ZFS 完整性》;
- 数据压缩与 layer 文件编码对应 《压缩算法工程实践》;
- 异步 IO 与高吞吐写路径对应 《Linux 异步 IO》 与 《io_uring》。
Serverless 不是魔法:它是把既有的存储技术重新组装起来,让用户看到”按量、按请求、按分支”的抽象。理解这些底层技术本身,比理解 Serverless 营销话术更有价值。
6.5 Serverless、HTAP、NewSQL 的关系
最后把它放回大图:
- NewSQL(《NewSQL》)的主线是横向分片 + 分布式事务;
- HTAP(《HTAP 新范式》)的主线是行列双维护 + 工作负载隔离;
- Serverless 的主线是存算分离 + 细粒度弹性。
这三条线并不互斥:TiDB Cloud 同时具备 NewSQL + HTAP + Serverless 特征;Aurora Serverless 是 Serverless + 单主 OLTP;PlanetScale 是 NewSQL + Serverless。读论文时把一个系统映射到”在这三条线上各走多远”,比纠结它属于哪一类更准确。
七、Serverless 数据库的选型
7.1 什么时候选 Serverless
适合的场景:
- 流量非常突发:白天一千 QPS、晚上几乎为零、夜里批处理又拉到五千 QPS。Serverless 的按量计费直接省钱。
- 多租户 / 多环境:每个租户、每条 feature 分支都要一个独立数据库。分支 O(1) 创建在这里价值极大。
- 初创 / 低负载业务:流量不可预测,不想一开始就为”峰值”付钱。
不适合的场景:
- 持续满载:峰谷接近 1:1 的在线业务,Serverless 的”弹性溢价”会变成纯成本负担。预留实例通常更便宜。
- 对 p99 极敏感:冷启动与扩容瞬间的抖动,对高频交易等毫秒级业务难以接受。
- 硬件协同优化:需要精确绑核、NUMA、特定内核参数的工作负载,Serverless 的托管环境没法让你动这些。
7.2 选型时真正该问的几个问题
- 最低规模是 0 还是 0.5 ACU? 如果业务允许缩到 0,优先看能真正缩到 0 的产品(Neon、Aurora Serverless v2 的新版本)。
- 分支是 O(1) 还是拷贝? 分支语义对开发流程影响大,不要等上线后才发现 “clone 一次要半小时”。
- 冷启动 p99 是多少? 要求产品方给出压测数据,而不是营销页的”秒级”这种模糊描述。
- compute 与 storage 是不是分别计费? 对存储成本高、计算间歇的工作负载,分别计费能省很多钱。
- 有没有 PITR(Point-in-Time Recovery)? 存算分离天然支持 PITR(WAL + 基线映像),但不同产品的回溯窗口、回溯粒度差别很大。
7.3 工程踩坑
- 连接数:Postgres 的
max_connections依然是硬约束,Serverless 不代表可以无限开连接。中间要配连接池代理。 - 未缓存页面的”第一次读”:compute
冷启动或扩容后,buffer pool 空的,查询会触发大量
get_page远程调用。对吞吐敏感场景要做 warmup。 - 长事务 vs 自动缩容:长事务会阻止 autovacuum,也会让 Serverless 无法缩容。业务层要尽量避免跨分钟级的长事务。
- 跨区域延迟:compute 与 pageserver 必须同 region,跨 AZ 的延迟尚可,跨 region 的话访问每一页都要几十毫秒。
7.4 成本模型的几个陷阱
Serverless 的营销词是”按用量付费”,但真实账单往往包含以下几类成本,不同产品切分方式差别很大:
- 计算:按 ACU × 时间,最小单位若干秒;
- 存储:按数据量 × 存储时长,冷数据下沉到对象存储后价格降一档;
- WAL / 日志保留:PITR 窗口越长,日志保留成本越高;
- 分支存储:每个分支自己写的页占额外空间;
- 出网流量:跨 region / 跨 AZ 访问常常单独收费;
- 连接 / API 调用:部分产品按 API 调用数或连接数计费,长连接池能显著省钱。
见过的真实踩坑:用 Neon 做 CI 给每个 PR 开分支,PR 合并后没删分支,几个月后分支存储费用超过主库。修复:CI 脚本在 PR 关闭时调用 API 自动删分支。这类”操作负担”在传统单体数据库下不存在,在 Serverless 下成为新的运维项。
7.5 可观测性
Serverless 数据库的可观测性面临两个新问题:
- 自动伸缩的尾延迟来自何处? 要能区分”我的慢查询”与”扩容瞬间的抖动”。产品方应暴露 ACU 变化事件、pageserver 命中率、冷启动次数等指标。
- 多租户环境下的干扰? 共享 pageserver / safekeeper 的租户间,性能互相干扰是否存在?如何证明?这要求产品提供 per-tenant 指标而不是聚合数字。
如果某个 Serverless 数据库不提供这些指标,在选型时应视为减分项——遇到事故时你将无法归因。
八、小结
Serverless 数据库不是一个新的数据库引擎,而是存算分离 + 共识化日志 + 细粒度资源调度三件事的组合:
- 存算分离是前提,让计算节点无状态;
- 共识化日志(Aurora 的 6 副本存储、Neon 的 safekeeper 共识)保证事务持久化不依赖计算节点;
- 细粒度资源调度(ACU、容器池)保证秒级伸缩。
冷启动的每一毫秒都可以追溯到这三个子系统里的某个环节;分支 O(1) 的能力则来自 copy-on-write 的存储层元数据设计。读懂 Aurora 论文与 Neon 的架构,再对照这些拆解,任何一款自称 “Serverless” 的产品都可以用同一把尺子去量。
另一个常被忽略的视角是:Serverless 数据库的最大价值未必是账单上的节约。对许多团队而言,它带来的真正改变是”弹性与分支能力改变了开发流程”——CI 给每个 PR 拉一个独立数据库、测试工程师在分支上做破坏性实验、数据科学家在生产分支上跑模型训练。这些用法在传统数据库下要么不可能,要么代价高到没人做。Serverless 把成本降到接近零,让数据库从单纯的”基础设施”变成”开发平台”。这是工程文化层面的改变,对效率的提升常常大于直接的硬件成本节约。
下一篇《Disaggregated DB 合集》会把这条线拉长,把 Aurora、Socrates、PolarDB、Taurus 放在一起比较,看看”日志即数据库”在四家不同团队的手里长成什么样子。
参考文献
- Verbitski A., et al. Amazon Aurora: Design Considerations for High Throughput Cloud-native Relational Databases. SIGMOD 2017. https://www.allthingsdistributed.com/files/p1041-verbitski.pdf
- Verbitski A., et al. Amazon Aurora: On Avoiding Distributed Consensus for I/Os, Commits, and Membership Changes. SIGMOD 2018.
- Khlystov N., et al. Neon: Serverless PostgreSQL. arXiv & Neon 技术博客. https://neon.tech/blog
- Neon 开源代码仓库. https://github.com/neondatabase/neon
- AWS Documentation. Aurora Serverless v2. https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.html
- PostgreSQL Documentation. Storage Manager
(smgr). PostgreSQL 源码
src/backend/storage/smgr/.
上一篇:【数据库研究前沿】HTAP 新范式:从 TiDB、SingleStore 到 Lakehouse 一体化
下一篇:【数据库研究前沿】Disaggregated DB 合集:Socrates、PolarDB、Taurus 的共同模式
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【数据库研究前沿】自治数据库十年回顾:Peloton、NoisePage、OtterTune 到云原生 auto-tuning
从 Peloton、NoisePage、OtterTune 到 Aurora / Azure SQL 的自动索引推荐,系统回顾自治数据库十年,并讨论云上 auto-tuning 的踩坑与 SRE 工作流集成
【数据库研究前沿】Disaggregated DB 合集:Socrates、PolarDB、Taurus 的共同模式
从 Aurora 到 Socrates、PolarDB、Taurus:系统梳理四家云数据库的存算分离架构共同点与差异——日志即数据库、页面服务器、缓存层级、故障恢复与工程踩坑
【数据库研究前沿】系列导论:从 System R 到 AI-Native 的 2026 研究地图
以 System R、Postgres、Bigtable、Spanner、Snowflake 等关键节点串起 50 年数据库史,勾勒 2026 年 AI-Native、向量检索、HTAP 云原生、新硬件、隐私计算、新范式、方法论七条主线,并给出 25 篇系列文章的完整阅读地图。
【数据库研究前沿】如何读数据库顶会论文:SIGMOD/VLDB/CIDR 阅读路线
从顶会定位、检索渠道、三遍读法到工业与学术论文的辨别方法,给出 2023–2025 年数据库领域可信必读二十篇,并配套 CMU 15-721、Stanford CS 245 等公开课清单。