LMDB 1.0:闪电内存映射数据库管理器
LMDB(Lightning Memory-Mapped Database Manager)是一个基于 Btree 的数据库管理库,其设计大致借鉴了 BerkeleyDB API,但大幅简化。整个数据库通过内存映射暴露给应用程序,所有数据读取操作都直接从映射内存中返回结果,查询过程中无需调用 malloc 或 memcpy。正因如此,该库极其轻量——它不需要自身的页缓存层,同时拥有极高的性能和内存效率。LMDB 支持完整的事务能力,具备完全的 ACID 语义;当内存映射以只读方式打开时,即便应用程序代码中存在野指针写入,也不会损坏数据库的完整性。
经过多年的 0.9.x 版本迭代,LMDB 1.0 是首个稳定版。
核心架构
线程与进程安全
LMDB 库完全感知多线程环境,支持来自多个进程和线程的并发读写访问。
写时复制策略
数据页面采用写时复制(Copy-on-Write, COW)策略,因此任何活跃数据页面都不会被原地覆盖。这一机制提供了出色的抗损坏能力,系统崩溃后无需任何特殊的恢复流程。
写入串行化
写入操作是完全串行化的:任何时候只能有一个写事务处于活跃状态,这保证了写入者永远不会发生死锁。
无锁读取
数据库结构采用多版本并发控制(MVCC),因此读取操作完全无需加锁——写入者不会阻塞读取者,读取者也不会阻塞写入者。
零维护开销
不同于其他常见的数据库机制(如预写事务日志或仅追加写入),LMDB 在运行期间无需任何维护操作。预写日志系统和仅追加数据库都需要定期检查点(checkpoint)或压缩(compaction)。LMDB 在数据库内部跟踪空闲页面,并将其重新用于新的写入操作,因此在正常使用中数据库大小不会无限制增长。
内存映射模式
内存映射可作为只读或读写映射使用。只读模式提供完全的防损坏能力;读写模式提供更高的写入性能,但也引入了应用程序意外写入可能静默损坏数据库的风险。
注意事项与最佳实践
锁文件
损坏的锁文件可能导致同步问题。程序异常中止后遗留的过期读取者事务,会导致后续写入操作迅速膨胀数据库大小;过期的锁也可能阻塞后续操作。修复方法:定期使用 mdb_reader_check 函数或 mdb_stat 工具检查过期读取者。
只读模式
通常情况下不存在纯粹的只读模式,因为读取者需要对锁和锁文件拥有写入权限。例外情况:在只读文件系统上,或使用 MDB_NOLOCK 标志时。
地址空间
LMDB 的配置通常会为未来的增长预留大量未使用的内存地址空间和文件大小。这并不会消耗实际的内存或磁盘空间。
多进程使用注意事项
- 每个线程一次只能使用一个事务
- 只在打开
MDB_env的进程中使用它,不要在fork()之后使用 - 不要在同一个进程中同时重复打开同一个 LMDB 数据库
- 避免长时间运行的事务。读取事务会阻止新的写事务释放的页面被重用
- 避免挂起包含活跃事务的进程
- 不要在远程文件系统上使用 LMDB 数据库
关于 LMDB
LMDB 库由 Howard Chu(Symas Corporation)开发,以 OpenLDAP Public License 发布。其代码源自 Martin Hedenfalk 的 btree.c,在此基础上发展而来。