【SQLite 内核】Rollback Journal 模式:写前拷贝与提交点
内容提要
本文介绍SQLite的rollback journal机制,即写前拷贝的原子提交方式。它通过五级锁状态机管理写事务,将原始页备份到-journal文件,提交点通过删除、截断或清零journal头部实现。只有发生cache spill时才会产生hot journal,崩溃后下次打开数据库时自动恢复。与WAL模式不同,rollback journal写会短暂阻塞读。
延伸解读
提交点的本质:文件存在性与头部合法性
rollback journal 的原子性依赖于一个简单的判断:journal 文件是否存在且头部合法。存在且合法意味着事务未提交,不存在或头部失效则视为已提交。DELETE、TRUNCATE、PERSIST 三种模式只是实现这一判断的不同手段,并非性能调优的同义词。理解这一点有助于避免误将 journal 文件的存在等同于未提交事务,尤其在 TRUNCATE 和 PERSIST 模式下,提交后文件依然存在。
cache spill 决定 hot journal 的产生时机
并非所有未提交事务都会产生 hot journal。只有当脏页因缓存溢出被迫提前写入磁盘时,才会生成头部合法的 journal 文件。小事务可能全程停留在内存,不产生 journal。因此,不能仅凭文件系统中是否存在 -journal 来判断是否有写事务在进行。判断当前写者应依据锁状态,而非 journal 文件的存在性。
崩溃恢复是自动的,但手工删除 journal 风险极高
崩溃后,只要 journal 是 hot 的,下一次打开数据库时会自动执行恢复,无需人工干预。但若有人误将 -journal 当作垃圾文件手工删除,会导致 hot journal 消失,主文件永久停留在崩溃时的中间状态,破坏数据库完整性。因此,运维中应避免使用非 SQLite 手段清理 journal 文件,尤其在 PERSIST 模式下。
写阻塞读的根源:EXCLUSIVE 锁的独占性
rollback journal 模式下,写者获得 EXCLUSIVE 锁后,新的读者会被完全阻塞,直到锁释放。这是该模式写会短暂阻塞读的直接原因。与 WAL 模式相比,WAL 通过追加日志避免了这一窗口,允许读写并发。理解这一差异有助于根据应用场景选择合适的 journal 模式。
Q&A
SQLite的rollback journal模式是如何实现原子提交的?
SQLite的rollback journal模式通过写前拷贝实现原子提交:在修改主数据库文件之前,先将原始页备份到-journal文件中,然后在提交点通过删除、截断或清零journal文件头部来使备份失效,从而保证事务的原子性。
SQLite的rollback journal模式有哪几种提交点实现方式?它们有什么区别?
SQLite的rollback journal模式有三种提交点实现方式:DELETE模式(删除journal文件)、TRUNCATE模式(将journal文件截断为0字节)、PERSIST模式(将journal文件头部清零但保留文件)。DELETE模式提交后journal文件消失,TRUNCATE模式提交后journal文件存在但大小为0,PERSIST模式提交后journal文件存在且大小不变但头部已清零。
SQLite中hot journal是什么?什么时候会产生?崩溃后如何恢复?
Hot journal是指存在且头部合法、大小超过512字节、且主数据库文件上没有RESERVED锁的journal文件。它通常发生在事务发生cache spill(缓存溢出)时,即事务修改的页超过内存缓存容量,被迫提前写入磁盘。崩溃后,下次打开数据库时,SQLite会自动检测到hot journal,并在读操作之前执行恢复流程:获取EXCLUSIVE锁,将journal中的原始页写回主文件,截断主文件到原始大小,然后删除journal文件,使数据库恢复到崩溃前的一致状态。
SQLite的rollback journal模式与WAL模式在读写并发上有什么不同?
在rollback journal模式下,写者获取EXCLUSIVE锁后会阻塞新的读者,直到提交完成释放锁,因此写操作会短暂阻塞读。而WAL模式中,写者只追加到-wal文件,不直接修改主文件,读者和写者可以同时进行,官方文档称“读者不阻塞写者、写者不阻塞读者”,但存在例外情况。
SQLite的journal文件头部格式是怎样的?如何判断journal文件是否有效?
SQLite的journal文件头部包含8字节魔数(0xd9 0xd5 0x05 0xf9 0x20 0xa1 0x63 0xd7),随后是页计数、校验随机数、原始数据库页数、扇区大小、页大小等字段。判断journal文件是否有效的标准是:文件存在且头部合法(魔数正确且头部未被清零)。如果头部被清零(如PERSIST模式提交后),则视为无效,不会被当作hot journal进行恢复。
SQLite的rollback journal模式中,cache spill对事务的journal文件产生有什么影响?
如果事务修改的页能全部装入内存缓存,SQLite会一直将改动保留在内存中,直到提交时才生成journal文件。只有当缓存被改动撑满,发生cache spill(缓存溢出)时,SQLite才会提前将journal文件写入磁盘,并可能产生hot journal。因此,小事务可能全程不产生journal文件,而大事务在提交前就可能产生journal文件。
SQLite的rollback journal模式与ARIES恢复机制有什么不同?
SQLite的rollback journal模式采用影子页(shadow paging)和写前拷贝的方式,每次写事务备份原始页,提交时销毁备份,用单写者约束换掉对细粒度日志记录和并发恢复的需求。而ARIES(Mohan et al., 1992)采用写前日志(WAL)和REDO/UNDO机制,假设有独立的日志空间和检查点机制来支持细粒度并发恢复。SQLite选择前者,与其单进程、单写者的定位一致。