name: transaction-correctness description: Turso数据库中WAL机制、检查点、并发规则和恢复如何工作
事务正确性指南
Turso仅使用WAL(预写日志)模式。
文件:.db, .db-wal(无.db-shm - Turso使用内存中的WAL索引)
WAL机制
写入路径
- 写入者将帧(页面数据)追加到WAL文件(顺序I/O)
- COMMIT = 头部中db_size非零的帧(标记事务结束)
- 原始数据库在检查点之前保持不变
读取路径
- 读取者获取读取标记(mxFrame = 最后一个有效提交帧)
- 对于每个页面:检查WAL直到mxFrame,回退到主数据库
- 读取者在其读取标记处看到一致的快照
检查点
将WAL内容传输回主数据库。
WAL增长 → 触发检查点(默认:1000页) → 页面复制到数据库 → WAL重用
检查点类型:
- PASSIVE:非阻塞,停止在活动读取者需要的页面
- FULL:等待读取者,检查点所有内容
- RESTART:类似于FULL,同时重置WAL到开头
- TRUNCATE:类似于RESTART,同时将WAL文件截断为零长度
WAL索引
SQLite使用共享内存文件(-shm)作为WAL索引。Turso不使用 - 它使用内存数据结构(frame_cache哈希映射,原子读取标记),因为不支持多进程访问。
并发规则
- 一次一个写入者
- 读取者不阻塞写入者,写入者不阻塞读取者
- 检查点必须停止在活动读取者需要的页面
恢复
崩溃时:
- 第一个连接获取独占锁
- 从WAL重放有效提交
- 释放锁,恢复正常操作
Turso实现
关键文件:
连接私有 vs 共享
每个连接(私有):
Pager- 页面缓存,脏页面,保存点,提交状态WalFile- 连接的快照视图:max_frame/min_frame- 此连接快照的帧范围max_frame_read_lock_index- 此连接持有的读取锁槽last_checksum- 滚动校验和状态
跨连接共享:
WalFileShared- 全局WAL状态:frame_cache- 页面到帧索引(替代.shm文件)max_frame/nbackfills- 全局WAL进度read_locks[5]- 读取标记槽(带有嵌入帧值的TursoRwLock)write_lock- 独占写入者锁checkpoint_lock- 检查点序列化file- WAL文件句柄
DatabaseStorage- 主.db文件BufferPool- 共享内存分配
正确性不变式
- 持久性:COMMIT记录必须在返回成功前fsync
- 原子性:部分事务对读取者不可见
- 隔离性:每个读取者看到一致的快照
- 无丢失更新:检查点不能覆盖未提交的更改