前 15 篇已经把 SQLite
的单文件、Pager、B-Tree、VDBE、Journal/WAL、锁与事务、计划器与备份走通。对读过
PostgreSQL
内核 或 MySQL
InnoDB 内核 的读者,下一个风险不是「不懂
SQLite」,而是把服务器行存的心智模型原样套过来:把
shared_buffers 当成 cache_size、把
PG 的 WAL 当成 SQLite 的 WAL、把行级锁经验拿去解释
SQLITE_BUSY。
本文是系列第 16 篇,只做机制对照,不写选型决策树(留给 第 17 篇),也不写跨库吞吐排名。
- 用进程 / IPC、日志、锁、缓冲池四张表钉住差异与共同点。
- 标出「同名不同义」的陷阱词:WAL、snapshot、vacuum、buffer。
- 给出站内阅读对照路径:每一轴回链本系列与 PG/InnoDB 的对应篇。
本文是「SQLite 内核」系列第 16 篇(共 17 篇)。→ 系列目录
篇目 核心内容 第 15 篇 · 在线备份与 sqlite3_backup Backup API 与 cp语义差第 16 篇 · 与 PG / InnoDB 机制对照 进程、WAL、锁、缓冲池四轴 第 17 篇 · 选型与阅读地图 SQLite vs PG vs DuckDB vs RocksDB
版本锚定:本系列机制结论锚定 SQLite 3.45+ 官方文档与本机 3.53.2 实测路径;PostgreSQL / InnoDB 对照以站内已发布内核系列为准(PG 进程模型见 01-process-shmem;InnoDB 架构见 01-process-architecture)。本文不引入未在本站或官方文档核对过的性能数字。
一、对照轴总图
flowchart LR
subgraph sqlite ["SQLite embedded"]
LIB["Library in app process"]
FILE["One DB file + journal or WAL"]
SW["Single writer per file"]
LIB --> FILE
LIB --> SW
end
subgraph server ["PG / InnoDB server"]
PROC["Dedicated server processes or threads"]
NET["Network protocol + auth"]
MW["Multi-writer with row or page locks"]
BUF["Shared buffer pool"]
PROC --> NET
PROC --> MW
PROC --> BUF
end
| 轴 | SQLite | PostgreSQL | InnoDB |
|---|---|---|---|
| 部署形态 | 嵌入应用进程的库 | 独立 postmaster + backend | mysqld 内存储引擎 |
| 数据载体 | 单文件(+ journal/WAL 附属) | 多文件 / 表空间 | 表空间 / ibd |
| 写并发 | 文件级单写者 | 多写者 + 行/元组锁 | 多写者 + 行锁 / gap |
| 崩溃日志 | rollback journal 或 SQLite WAL | PG WAL + checkpoint | redo / undo |
| 缓存 | 每连接/进程 Page Cache | shared_buffers 共享 |
Buffer Pool 共享 |
二、进程模型与 IPC
SQLite
删掉了「数据库服务器进程」这一层:调用是函数调用,不是
socket。代价是库与宿主同生共死——应用
OOM、被杀、错误地直接改 .db
文件,都会直接打到数据面(第 1、14 篇)。
PostgreSQL 用多进程 + 共享内存:每个连接一个 backend,缓冲池与锁表在共享段(postgresql-kernel/01)。InnoDB 跑在 mysqld 线程模型上,连接与存储引擎通过服务器层解耦(mysql-innodb/01)。
flowchart TB
subgraph app ["Application"]
A1["Business code"]
end
subgraph emb ["SQLite path"]
A1 -->|"sqlite3_step"| VDBE
VDBE --> Pager
Pager --> File["db file"]
end
subgraph srv ["Server path"]
A2["Client lib"] -->|"SQL over socket"| Backend
Backend --> SharedBuf["Shared buffers"]
SharedBuf --> Disk["Tablespace / files"]
end
常见误解
「本机用 Unix socket 连 PG,延迟就和 SQLite 一类。」
仍有协议编解码、backend 调度与共享缓冲池锁协作;零 IPC 是架构差,不是「本机部署」能抹平的。「SQLite 没有并发,因为没有服务器。」
多读者、WAL 下读写重叠、多连接都存在;缺的是多写者同时改同一文件(第 9–10 篇)。
三、日志:同名 WAL,不同契约
| 问题 | SQLite WAL | PostgreSQL WAL | InnoDB redo |
|---|---|---|---|
| 目的 | 单文件库的写前日志 + 读写重叠 | 崩溃恢复 + 复制基础 | 崩溃恢复 |
| 读者可见性 | 读者可读 WAL 帧 + 主文件快照 | 读缓冲池/页;复制另议 | 读 Buffer Pool;undo 可见性 |
| 提交点叙事 | checkpoint 把帧合并回主文件 | WAL flush + 事务提交记录 | redo 落盘与事务提交协作 |
| 运维附属 | -wal / -shm |
pg_wal 段文件 |
redo 日志组 |
本系列第 7–8 篇钉住:rollback journal 的提交点是「journal
不再 hot」;WAL 模式则反转读写关系。把 PG 的
checkpoint、归档、复制槽经验直接搬到
PRAGMA wal_checkpoint,会错位——SQLite
没有复制拓扑这一层。
InnoDB 的 redo/undo 与 SQLite journal 更接近「服务器级原子提交与 MVCC 版本」;对照时只借用「写前留恢复信息」这一共性,不假装参数可互换。
四、锁与隔离
SQLite 锁阶梯是文件级(UNLOCKED → SHARED → RESERVED → PENDING → EXCLUSIVE,第 9 篇);事务修饰词改变的是取锁时机(第 10 篇)。官方 isolation 叙事强调写串行化,而不是服务器式的多版本写写冲突检测。
PostgreSQL / InnoDB 提供行级(及更细)锁与丰富的隔离级别实现;站内 mvcc 与两套内核系列展开可见性、gap lock、SSI 等。可平移的只有词汇(脏读、幻读、write skew);不可平移的是「默认能撑多少并发写」。
| 场景 | SQLite 常见现象 | 服务器行存常见现象 |
|---|---|---|
| 两写者抢同一库 | SQLITE_BUSY / 等待 |
行锁等待或死锁检测 |
| 长只读 | SHARED 可挡升 EXCLUSIVE(rollback);WAL 更友好 | 快照/VACUUM 交互另议 |
| 「SERIALIZABLE」标签 | 写串行化实现(官方 isolation) | 实现路径因引擎而异 |
常见误解(续)
「把
BEGIN IMMEDIATE当成 SERIALIZABLE 开关。」
它提前拿 RESERVED,减少后来升锁失败;隔离叙事仍见第 10 篇与官方 Isolation In SQLite。「SQLite 的 snapshot 等于 PG 的 snapshot isolation。」
WAL 读者看到的是一个检查点相关的稳定视图;没有多写者 SI 下的 write skew 戏台(第 10 篇)。
五、缓冲池与 Page Cache
SQLite Page Cache
服务本连接/本进程的页访问(第 3
篇);多进程打开同一文件时,靠 change counter 或 WAL-index
使缓存失效,而不是共享一块 shared_buffers。
PostgreSQL shared_buffers 与 InnoDB Buffer
Pool
是跨连接共享的服务器资源:命中率、刷脏、checkpoint
与服务器生命周期绑定。运维上「加大缓冲池」对 SQLite
的对应旋钮是 PRAGMA cache_size /
编译默认,且每个连接各自一份启发式缓存,不是集群级共享内存调参。
flowchart LR
subgraph sqlcache ["SQLite"]
C1["Connection A cache"]
C2["Connection B cache"]
F["Same db file"]
C1 --> F
C2 --> F
end
subgraph srvcache ["PG or InnoDB"]
SB["Shared buffer pool"]
TS["Tablespace"]
B1["Backend 1"] --> SB
B2["Backend 2"] --> SB
SB --> TS
end
六、站内对照阅读路径
| 你想对齐的问题 | 先读本系列 | 再读服务器系列 |
|---|---|---|
| 谁在跑、有没有 IPC | 第 1、5 篇 | PG 01-process-shmem;InnoDB 01 |
| 页与树 | 第 2–4 篇 | PG/InnoDB 页面与索引章 |
| 日志与提交 | 第 7–8 篇 | PG WAL 章;InnoDB Redo/Undo 章 |
| 锁与隔离 | 第 9–10 篇 | mvcc + 两套锁章 |
| 缓存 | 第 3 篇 | PG Buffer;InnoDB Buffer Pool |
| 备份与损坏 | 第 14–15 篇 | 各系列备份/PITR 章(语义不同) |
七、学术谱系、工程间隙与开放问题
谱系:三者都落在「页式存储 + 日志保证原子提交」的大传统上;SQLite 的分叉是嵌入式约束优先(Hipp 架构选择;Gaffney et al., PVLDB 2022 讨论其负载边界),PG/InnoDB 的分叉是多连接服务器与更细锁。B-Tree vs LSM 的另一条轴见 rocksdb 与本系列第 4、17 篇。
工程间隙:对照表描述的是机制角色,不是「谁更适合你的 QPS」。文件大小、写冲突率、是否需要网络多租户,会让同一机制轴上的优选翻转——这是第 17 篇的决策树,不是本篇能用一张表关闭的。
开放问题:嵌入式引擎是否应吸收更多服务器级细粒度锁,社区长期克制(第 1、9、10 篇);服务器引擎是否应提供「库级嵌入」形态(已有进程内扩展尝试,但不是本系列对象)。两边都没有「即将统一」的定论。
常见误解(收束)
- 「对照表可以当性能排名。」
机制不同则 benchmark 口径不同;本站不做未实测的跨库排名。
八、小结
三句话小结
- SQLite 用嵌入式库 + 单文件 + 文件级单写者,删掉了 PG/InnoDB 的服务器进程、网络协议与共享缓冲池。
- WAL、snapshot、cache、vacuum 等词在三者中同名不同义;运维与排障经验必须换契约再复用。
- 四轴对照用于建立坐标系;何时选哪一类引擎,见第 17 篇决策树。
参考资料
官方与站内机制(A 级)
- SQLite Architecture、WAL、Locking、Isolation In SQLite。
- 本系列第 1、3、7–10、14–15 篇。
- PostgreSQL 内核、MySQL InnoDB 内核、MVCC。
论文(A 级)
- Berenson et al., SIGMOD 1995(隔离词汇)。
- Gaffney et al., SQLite: Past, Present, and Future, PVLDB 2022(嵌入式负载讨论框架)。
站内
上一篇:在线备份与
sqlite3_backup
下一篇:选型与阅读地图
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【SQLite 内核】Pager 与 Page Cache
拆解 Pager 作为 B-Tree 与操作系统文件之间的契约层:page cache 命中/未命中路径、脏页生命周期与提交前的可回滚保证,并与 PG shared_buffers、InnoDB Buffer Pool 的跨进程共享模型对照。
【SQLite 内核】单文件 · Pager · B-Tree · VDBE · WAL · 锁
补齐嵌入式行存内核层:从单文件格式、Pager/B-Tree、VDBE 到 Rollback Journal/WAL、锁状态机与计划器,并以 PG/InnoDB、DuckDB、RocksDB 对照收束;承接 sqlite-billion-rows 性能叙事。
【SQLite 内核】嵌入式行存全景:单文件、单写者、零 IPC
定位单文件嵌入式行存在服务器行存与 LSM 嵌入 KV 之间的生态位;钉住 SQLite 架构约束、站内分工与 17 篇阅读路线,并以 Bayer/McCreight、官方 file format、PVLDB 2022 为学术锚点。
【SQLite 内核】WAL 与 checkpoint:追加日志与读写并发
钉住 SQLite WAL 模式如何反转 rollback journal 的读写关系:原始内容留在主文件、新内容追加到 -wal,checkpoint 才把帧搬回主文件;用本机 3.53.2 实测 journal_mode=WAL 切换与 passive/truncate checkpoint 的真实帧数,读放大与 checkpoint 频率的取舍留作开放问题。