本文按 TinyDbEngine 真实代码路径拆解启动流程,重点回答三个问题:
新库和旧库是如何区分的?
WAL 恢复何时触发?
初始化后哪些核心组件会被装配?
1. 构造函数入口
入口在:
TinyDbEngine(string f, TinyDbOptions? o = null)internal TinyDbEngine(string f, TinyDbOptions? o, IDiskStream? ds)
核心动作:
_options = o ?? new TinyDbOptions();
_options.Validate();
_pageManager = new PageManager(...);
_writeAheadLog = new WriteAheadLog(..., _options.EnableJournaling, ...);
_pageManager.RegisterWAL(lsn => _writeAheadLog.FlushToLSN(lsn));
_pageManager.RegisterWAL((lsn, ct) => _writeAheadLog.FlushToLSNAsync(lsn, ct));
_flushScheduler = new FlushScheduler(...);
_largeDocumentStorage = new LargeDocumentStorage(...);
_dataPageAccess = new DataPageAccess(...);
_metadataManager = new MetadataManager(this);
InitializeDatabase();2. InitializeDatabase() 五步走
InitializeDatabase() 是启动核心,代码逻辑可以总结为:
WAL 重放(可选)
文件结构初始化(新库)或头部读取(旧库)
PageManager 与 Header 状态对齐
系统页与集合元数据加载
安全验证(密码保护)
2.1 WAL 重放
当 EnableJournaling=true 时,启动会调用 WriteAheadLog.Replay(...)。
关键点:并不是无脑回放,每条日志都会比较 LSN:
若
walHeader.LSN <= diskHeader.LSN,跳过该日志。否则
RestorePage覆盖页面。
这保证了恢复幂等,不会因为重复回放破坏已更新页面。
2.2 新库初始化路径
当 _diskStream.Size == 0 时:
创建
DatabaseHeader。分配页 1(Header 页)。
PageManager.Initialize(1, 0)。
意味着数据库首次启动会完成最小页结构建库。
2.3 旧库加载路径
当文件已存在时:
从页 1 读取 Header:
ReadHeader()。校验
Header.IsValid()。PageManager.Initialize(_header.TotalPages, _header.FirstFreePage)。
随后有一个非常关键的同步修正:
如果
PageManager.TotalPages > _header.TotalPages或空闲页链表状态不一致,会WriteHeader()反向回写头部。
这一步专门处理“崩溃发生在 Header 写回前”的状态漂移。
2.4 系统页与集合元信息
初始化系统页:
CollectionInfoPage(集合信息)IndexInfoPage(索引信息)
再创建 CollectionMetaStore 并 LoadCollections()。
2.5 安全验证
EnsureDatabaseSecurity() 逻辑:
若未配置密码:数据库若已受保护 -> 拒绝访问。
若配置了密码且数据库已受保护:必须认证成功。
若配置了密码且数据库未受保护:创建安全元数据。
3. 启动后的运行态对象
初始化成功后,引擎至少具备以下能力:
页缓存与分配:
PageManagerWAL 写前日志:
WriteAheadLog后台持久化策略:
FlushScheduler大文档分离存储:
LargeDocumentStorage数据页读写与扫描:
DataPageAccess集合元数据管理:
CollectionMetaStoreSchema / 元数据:
MetadataManager事务调度:
TransactionManager
4. CompactDatabase() 的重建思路
CompactDatabase() 不是“页内整理”,而是“重写新文件再替换”:
Flush()先落盘。创建临时引擎写新库。
迁移集合数据与索引定义。
释放当前组件句柄。
文件替换后重新初始化组件。
优点:实现简单且稳定,代价是执行期间是重操作。
5. 对业务方的启示
打开数据库是重路径,避免在每次请求里
new TinyDbEngine。生产环境建议显式配置
TinyDbOptions,特别是WriteConcern、EnableJournaling、TransactionTimeout。开启密码后,错误密码会在启动阶段失败,不会等到首次查询再报错。
6. 小结
TinyDbEngine 启动逻辑的核心价值是:
通过 WAL + LSN 保障崩溃恢复幂等。
通过 Header 与 PageManager 状态对齐保障结构一致。
在启动期一次性装配核心组件,运行期只做业务读写。
下一篇进入最关键的性能路径:插入、更新、删除如何落盘。
发表评论