【免费下载链接】latticedb
Embedded single-file knowledge graph database with vector search and full-text search for AI/RAG apps
LatticeDB 是一款嵌入式的单文件知识图谱数据库,面向 AI/RAG 应用提供图遍历、向量检索与全文搜索。它的可靠性核心由三块拼图构成:ACID 事务(原子性由 WAL 日志保障)、快照隔离(基于 MVCC 多版本并发控制)以及单写者模型(同一时刻只允许一个写事务)。本文带你快速看懂这套持久化体系的设计逻辑与使用边界。
ACID 四属性如何落地?
LatticeDB 的事务管理器(src/transaction/manager.zig)通过"事务日志 + 提交后落盘"的组合实现经典四属性,设计细节见 docs/06_transaction_manager.md:
| 属性 | 含义 | 实现机制 |
|---|---|---|
| Atomicity 原子性 | 要么全做,要么全不做 | WAL 日志 + 回滚链(prev_lsn) |
| Consistency 一致性 | 从一个合法状态到另一个合法状态 | 应用逻辑约束 |
| Isolation 隔离性 | 事务互不干扰 | 时间戳 + MVCC 快照 |
| Durability 持久性 | 提交即永久 | WAL + fsync 落盘 |
每个事务在BEGIN时获得全局递增的start_ts,COMMIT时获得commit_ts——这两个时间戳就是快照可见性的裁判(下文详述)。此外,事务还支持Savepoint(保存点):可以只回滚事务的一部分操作,而不必推倒重来。
💡 读事务(read-only)是一个"轻量档":不写 WAL、不加锁、永远不会因冲突中止,天然适合读多写少的 RAG 场景。
单写者模型:一个写事务 + N 个读事务
这是新手最容易踩坑的地方 ⚠️
- 每个数据库句柄同一时刻只允许一个打开的写事务;
- 尝试开启第二个写事务时,不会排队等待,而是立即失败,返回锁超时报错(该行为自 0.10.0 起生效,把隐晦的竞态变成显式错误);
- 读事务不受影响,可以与写事务并行运行,读不阻塞写、写不阻塞读。
各语言中的报错形式(来自 book/src/guides/transactions.md):
| 语言 | 报错 |
|---|---|
| C | LATTICE_ERROR_LOCK_TIMEOUT(-8) |
| Python | LatticeLockTimeoutError |
| TypeScript | LatticeError,.code === LatticeErrorCode.LockTimeout |
| Go | ErrorLockTimeout |
正确姿势:与其捕获错误重试,不如把多个变更合并进一个事务批量提交——既避免报错,又因为持久化开销只发生一次而更快。多线程写入时,用锁、队列或单一写者任务把写操作串行化。
还有一层更粗的规则:数据库文件同一时刻只能被一个进程打开。读写句柄独占文件锁,只读句柄共享锁。若冲突,打开时就会报LATTICE_ERROR_DATABASE_LOCKED(Python 中为LatticeDatabaseLockedError),而不是等到数据损坏才发现问题。
快照隔离:MVCC 让读者互不干扰
LatticeDB 内部事务管理器采用快照隔离的 MVCC 机制,核心实现在 src/transaction/mvcc.zig:
- Snapshot:事务开始时拍下的"数据库照片",记录
read_ts和当时仍在运行的事务列表; - VersionInfo:每个数据版本携带
created_by(创建事务)、commit_ts(提交时间戳,未提交为 0)等元数据; - 可见性规则(isVisible 函数):
- 自己写的变更永远可见(即使未提交);
- 他人未提交的版本不可见;
- 快照之后才提交的版本不可见;
- 快照时刻仍在运行的事务,其版本一律不可见。
举个经典时间线:事务 A 在 ts=4 提交x=100,而事务 B 在 ts=3 已开启——B 就"看不见"这个新值,它看到的是 A 写入前的状态。
ts=1 ts=2 ts=3 ts=4 ts=5 │ │ │ │ │ A BEGIN A写x=100 B BEGIN A COMMIT B读x → 看不到x=100为防止旧版本无限堆积,版本链配有垃圾回收逻辑(src/transaction/mvcc.zig):只有当"已有更新版本"且"最老的活跃事务也不需要它"时,旧版本才会被清理。
说明:引擎级契约见 docs/13_engine_conformance.md。当前公开的硬承诺是:事务能看到自己的未提交写、写事务提交后新事务可见新状态、回滚后的变更不可见;快照隔离是内部管理器的目标语义,实现可以提供更强保证,但跨引擎移植时不要依赖未冻结的部分。
WAL 预写日志:持久性的真正来源
LatticeDB 遵循数据库界最经典的规则:先写日志,再改数据(write-ahead)。完整原理见 docs/05_write_ahead_log.md,实现见 src/storage/wal.zig。
INSERT → 分配LSN → 追加到WAL帧缓冲 → (帧满 或 COMMIT) → 刷盘+fsync → 才返回"已提交"几个关键设计:
- LSN(日志序列号):单调递增的"全局时钟",为每条记录定序,也是恢复的起点;
- prev_lsn 链:每条记录指向同一事务的上一条记录,形成向后链表——回滚时沿链撤销,恢复时无需全量扫描日志;
- 4KB 帧 + CRC32 校验:按页对齐降低"撕裂写"风险,校验和可识别损坏帧;
- UUID 绑定:WAL 头部记录数据库文件的 UUID,防止张冠李戴;
- fsync 才是终点:只有
fsync()返回后,COMMIT才向应用报告成功。这就是持久性保证。
所以"提交"很快(一次顺序写日志),而数据库文件本身由检查点异步更新——这正是"快而持久"的秘诀。
⚠️ 注意:enable_wal = false不是性能开关。WAL 关掉后事务整体不可用(beginTransaction直接返回TransactionsNotEnabled),没有 BEGIN、没有回滚、没有多语句原子性。详见 book/src/configuration/durability.md。
检查点:让日志有界、恢复更快
没有检查点,WAL 会无限增长,崩溃后要重放一年日志。检查点(src/storage/checkpoint.zig,设计见 docs/07_checkpointing.md)把脏页刷入数据库文件,并推进checkpoint_lsn——恢复从此处开始,老日志即可丢弃。
| 模式 | 行为 | 适用场景 |
|---|---|---|
passive | 能刷就刷,跳过被占用的页,不阻塞事务 | 后台周期性检查点 |
full | 等待锁、刷全部脏页 | 关闭数据库前 |
truncate | full 之后把 WAL 截断重置 | 回收磁盘空间 |
自动检查点由auto_checkpoint控制,默认参数:
| 参数 | 默认值 | 作用 |
|---|---|---|
max_wal_frames | 1000 | 写满多少帧触发检查点 |
min_interval_ns | 0 | 两次检查点的最小间隔 |
mode | truncate | 唯一能真正限制日志大小的模式 |
CLI 也提供手动触发:lattice checkpoint social.lattice——在复制数据库文件之前执行一次,能确保副本自包含、完整可用。
崩溃恢复:两阶段 Redo,无需 Undo
恢复流程借鉴 IBM 的 ARIES 思想(docs/08_recovery.md,实现见 src/storage/recovery.zig):
- Analysis(分析):从
checkpoint_lsn开始扫描 WAL,判断每个事务的最终状态(committed / aborted / in_progress); - Redo(重做):只重放已提交事务的操作,未提交的直接丢弃。
LatticeDB 做了一个巧妙的简化——跳过 Undo 阶段:事务运行期间变更只停留在内存/日志中,提交时才应用到数据页。崩溃时内存里未提交的变更直接蒸发,自然无需反向撤销。
损坏检测也很有意思:遇到校验和失败时,恢复器会向前扫描 10 帧:
- 后面没有有效帧 → 判定为尾部撕裂写(安全,到此为止);
- 后面还有有效帧 → 判定为日志中段真实损坏(危险!直接失败,宁可要求从备份恢复,也不悄悄丢数据)。
实战避坑清单 🧭
| 场景 | 会发生什么 | 建议 |
|---|---|---|
| 嵌套开启两个写事务 | 第二个立即抛LockTimeout | 合并为一个事务,或用单写者任务串行化 |
| 打开时另有一进程占用 | DatabaseLocked错误 | 先关闭对方;网络文件系统可显式关锁(自负风险) |
打开期间直接复制.lattice文件 | 副本不完整(日志里还有未落盘变更) | 先lattice checkpoint,或用备份工具 |
| 进程崩溃 / 断电 | 下次打开自动重放 WAL | 已提交数据不丢 |
| 关闭后删除 WAL 文件 | 未检查点的已提交变更丢失 | 别手动删日志 |
总结
LatticeDB 用一套极简但完整的机制撑起了嵌入式数据库的可靠性底线:
- WAL + fsync保证"提交即持久";
- prev_lsn 链 + 两阶段 Redo让崩溃恢复又快又安全;
- 检查点给日志封顶、给恢复提速;
- MVCC 快照隔离让并发读零阻塞;
- 单写者模型把复杂的并发写冲突变成一条清晰的显式错误。
延伸阅读:
- 事务与持久化指南:book/src/guides/transactions.md
- WAL 结构:book/src/architecture/wal.md
- 检查点与恢复:book/src/architecture/checkpointing.md、book/src/architecture/recovery.md
- 引擎一致性契约:docs/13_engine_conformance.md
【免费下载链接】latticedb
Embedded single-file knowledge graph database with vector search and full-text search for AI/RAG apps
相关推荐
深度解析ZITADEL数据库事务实践:5大ACID特性保障与隔离级别应用
深度解析ZITADEL数据库事务实践:5大ACID特性保障与隔离级别应用 ZITADEL作为现代身份基础设施,其数据一致性保障依赖于完善的数据库事务管理。本文将
后端认证鉴权身份认证单点登录campus-imaotai i茅台自动预约系统:Docker 一键部署的每日自动预约方案
campus imaotai i茅台自动预约系统:Docker 一键部署的每日自动预约方案 一、它解决什么问题 每天上午 9 点,i茅台的申购窗口准时开放,人工
后端任务调度前端工作流自动化电商JW Player终极教程:如何5分钟实现网页视频播放
JW Player终极教程:如何5分钟实现网页视频播放 JW Player是一款功能强大的网页视频播放解决方案,已在超过200万个网站上使用,每月播放量达13亿
音视频前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考