跳到主要内容
LMDB 的文档页脚,停在 2012 年 9 月

LMDB 的文档页脚,停在 2012 年 9 月

嘴替
嘴替

· 阅读约 4 分钟

速评:LMDB 的文档页脚写着,由 Doxygen 1.8.2-20120930 于 2026 年 6 月 30 日生成。

十四年前的生成器,今年六月还在跑。

这行小字比正文里任何一句都更说明这个库的态度。这几年技术圈换工具链跟换衣服一样勤,一个能把 2012 年九月版本的 Doxygen 一路用到今天的项目,你指望它在文档里跟你讲"重新定义"?不现实。顺带,这份文档对应的是 1.0,0.9 那版已经归档到别处,只留了一份升级说明。

反过来看才有意思:文档里写满了当下发布会最爱当卖点讲的那几样东西,但没有一处是当卖点写的。

读路径不分配内存、不做拷贝,数据直接从内存映射里返回;写时复制,活跃页永不被覆盖,所以崩溃之后不需要一套专门的恢复流程;没有 WAL,没有 checkpoint,没有 compaction——原文就一句,跟预写日志和只追加写入的方案不同,运行期不需要这些维护;写事务完全串行化,因此写者之间不存在死锁;多版本结构,读不加锁,写不阻塞读,读也不阻塞写。

这几条你现在去翻任何一个"下一代 agent 记忆层"的项目首页,能翻出七八成。区别是那边叫 slogan,这边叫条款。

这剧本,眼熟——每隔几年就有人把存储工程的老常识重新包一层,卖一轮。

但让我动笔的不是夸它。夸一个活了十五年的库没什么意思,我也不是"老东西就是好"那一派,库活得久不等于它适合你,这话我平时讲得比谁都响。

这份文档对我有用,是因为它把"你会怎么弄坏它"写在明面上。

最长的那根刺上挂着第一个坑:长时间存活的读事务。读事务会拦住较新写事务释放出来的页面被复用,数据库因此快速膨胀。翻译一下——一个从会话开头就一直开着、中间不关的读快照,能让文件体积涨到跟你的实际数据量完全不成比例。这不是"偶尔会慢",是文件越来越胖,而且越读越胖。

第二个坑更狠:不应在事务活跃时挂起进程。原因是读事务如果在写者提交期间被挂起,有时可能读到错误数据。我最想拍桌子的就是这条——现在最时兴的那套部署形态,容器快照、进程冻结再唤醒、把运行状态整个存下来再恢复,干的就是这件事,而且干得理直气壮。文档里这句话写得很平静,没加粗,旁边也没配一张"注意"的示意图。它默认你是来看条款的,不是来看海报的。

第三:不要把数据库放在远程文件系统上,即使是同一台主机上的多个进程也不要。理由给得也干脆——某些系统上的 flock 会被破坏,内存映射的同步会受影响,跨主机程序之间肯定对不上。所有打算把本地嵌入式库的文件丢到网络盘上做共享的路子,到此为止。

再加一条反直觉的:这库没有真正的纯只读模式。读操作也要对锁文件和锁做写访问。例外只有两个,只读文件系统,或者打开时带上 MDB_NOLOCK。我第一次看到这条愣了两秒:一个自称读路径零拷贝的库,读的时候还得写文件。但这就是它的账本,摊开给你看。

默认值上的取向也统一得有点古板。内存映射默认只读——应用里一个野指针写过去,写不进只读映射,破不了数据完整性;想快就自己切读写模式,代价是野指针可能静默把库写坏。0.9.10 起,写入前默认初始化未使用的内存,就为了不让别处 free 掉的垃圾数据混进数据文件,代价是性能开销,想省这点开销就开 MDB_NOMEMINIT——但处理敏感数据的应用不该开这个开关。两条是同一个选择:默认站在数据安全那侧,快的那档留给你自己动手打开。这两年的默认值取向经常是反的——默认全开、默认最激进,等你踩了坑再去翻有没有