4.2 journal 模式五态


4.2 journal 模式五态与取舍

本节摘要PRAGMA journal_mode 决定日志的物理形态:DELETE、TRUNCATE、PERSIST、MEMORY、WAL 五种模式是五套落盘策略。本节逐一拆解每种模式下"一次提交"的磁盘行为,给出对照决策表,并厘清它与 synchronous 参数的分工——一个管"日志长什么样",一个管"等多久才算提交"。

五种模式的落盘行为

四种回滚系模式共享 4.1 节的账本逻辑,差别只在"账本文件怎么处理":

模式 提交后 journal 的命运 断点恢复依据 磁盘行为特征
DELETE(默认) 整个删除 文件不存在即无未完事务 反复创建删除文件,目录元数据开销大
TRUNCATE 截断为零字节 头部零即视为作废 免目录操作,适合嵌入式闪存
PERSIST 文件保留,头部字段抹零 头部校验字段失效即作废 免目录与尺寸变更,重用同尺寸文件
MEMORY 日志只存内存 无——断电即丢回滚能力 最快但破坏持久性,仅测试用
WAL 没有回滚 journal,改用 WAL 追加 WAL 盐值与校验和 读写并行,检查点回写主文件

三种"文件保留"变体(TRUNCATE、PERSIST)的存在理由主要是嵌入式与闪存设备:创建和删除文件需要修改目录项,在 SD 卡类的存储上既慢又加剧磨损。把它们改成"反复覆写同一个文件",省掉的是元数据操作。桌面与服务器场景的闪存同样受益——这也是不少移动端框架默认选 TRUNCATE 或 WAL 的原因。

切换动作本身值得演示:

PRAGMA journal_mode; -- 查询当前模式:delete PRAGMA journal_mode = WAL; -- 返回 wal 表示切换成功 PRAGMA journal_mode; -- 确认:wal

注意两件事。其一,切换到 WAL 是持久设置——它写进了第 1 章读过的文件头 18/19 字节(读写版本号变成 2),下次打开仍是 WAL;切回 DELETE 同理。其二,切换必须在没有未完成事务时进行;从任何模式切到 MEMORY 都是危险动作,等于宣布"我不再要求崩溃一致性"。

WAL 与回滚系的结构差异

切换到 WAL 后,写入路径发生结构性变化:提交时新页不再覆盖主文件,而是追加<数据库文件名>-wal 伴生文件;主文件在检查点(checkpoint)之前保持旧世界。这个一换带来三连变化:

  1. 读写不再互斥。读者继续读旧世界的主文件,写者把新世界追加到 WAL,互不干扰。并发能力从"单读者或单写者"跃迁为"多读者加一个写者"。
  2. 提交更快。追加是顺序写,且只写新页不搬旧页;普通提交从"两轮等待"变成"一轮 WAL 落盘"。
  3. 多出一个要管的文件。WAL 会增长,靠检查点回收;数据库文件的冷拷贝从此必须连同 WAL 与 shm 一起拷,否则可能拷到一个"缺了尾部事务"的旧世界。

图:journal 与 WAL 模式的读写并发对比

图:journal 与 WAL 模式的读写并发对比

synchronous:另一个旋钮

journal 模式回答"日志长什么样",synchronous 回答"等多久才算提交",两者正交组合:

synchronous 行为 WAL 模式下的实际含义
FULL(默认回滚系) 每次提交等待 fsync 最安全,最慢
NORMAL 关键时机等待 WAL 下推荐:断电最多丢最近事务,库不损坏
OFF 几乎不等 可能在断电时损坏文件,仅可丢弃数据可用

WAL 模式下官方明确推荐 synchronous=NORMAL:因为 WAL 是顺序追加日志,即使最近的事务在断电中丢失,主文件与 WAL 的结构仍完整,库一定能打开——这与回滚系模式下 OFF 的风险等级完全不同。移动端与桌面应用的标准组合是 journal_mode=WALsynchronous=NORMAL

⚠️ 常见误操作:把 MEMORY 模式当性能优化用在生产库上。它确实快,但断电后数据库文件可能处于不可恢复的损坏状态——省下的是毫秒,赔上的是整库。

常见问题速答

**切到 WAL 返回的还是 delete,为什么?**两个常见原因。一是连接里有未完成的事务——切换必须在事务边界上,把活动事务收尾再切。二是文件系统不支持 WAL 依赖的共享内存语义(常见于网络盘与部分虚拟化挂载),引擎会拒绝切换并保持原模式;这种环境下 WAL 也确实不该用,4.3 节的检查点机制在共享内存缺失时无法保证正确性。

**移动端框架为什么几乎都默认 WAL?**移动负载是典型的"界面读加后台写"并发形态,WAL 让两者互不拖累;顺序追加对闪存寿命友好;NORMAL 同步级别在设备断电(电池拔出、崩溃重启)场景下只丢最后事务不损坏库——三条好处都精确命中移动端痛点。桌面浏览器的 WebAssembly 环境同样把 WAL 作为推荐配置。

**OFF 模式什么时候用?**只用于两类数据:完全可重建的数据(缓存表、可重算的派生表)和一次性测试库。它的"快"来自根本不记账——不仅断电丢事务,任何崩溃都可能留下结构损坏。把 OFF 当性能优化用在不满足这两个前提的库上,省下的是毫秒,赌上的是整库。

本节要点回顾

  • 五种 journal 模式是五套落盘策略:三种保留文件的变体为闪存省元数据操作,WAL 换一种日志架构。
  • WAL 的三个结构性收益:读写并行、顺序追加提交更快、代价是多一个要管理的伴生文件。
  • journal_mode 管"日志形态",synchronous 管"等待时机",WAL 加 NORMAL 是嵌入式场景的标准组合。
  • 切换 journal_mode 写进文件头,是持久设置;WAL 在网络文件系统上不可用。

**PERSIST 与 TRUNCATE 怎么选?**两者都保留 journal 文件,差异在提交后的处理:TRUNCATE 把文件截为零字节(尺寸变化,但目录项不动),PERSIST 只抹掉文件头的有效标记(连尺寸都不变)。闪存上两者都避免了创建删除的元数据写;PERSIST 更极致——连文件系统的长度变更都省了,代价是磁盘上长期躺着一个与库等大的 journal 占位文件。空间敏感的设备选 TRUNCATE,写放大敏感的选 PERSIST。

**一个事务里能混用模式吗?**不能,journal_mode 是库级持久属性,切换必须在事务边界完成。但库与库可以不同模式——ATTACH 多个库时,主库 WAL、附属库 DELETE 是合法组合,只是跨库事务会退化为各库独立日志的协调模式,性能与原子性保证都弱于单库事务。需要跨库一致性的场景,先考虑合并成单库加命名空间隔离(表前缀或 schema 约定),再考虑 ATTACH。

下一节进入 WAL 文件内部:帧怎么组织,检查点怎么回收,盐值和校验和各防什么。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U