跳到主要内容

LMDB

LMDB(Lightning Memory-Mapped Database,闪电内存映射数据库)是Kumo采用的嵌入式键值存储系统,专门用于存储事务性、高性能的本地元数据及小型控制平面数据。

本文档结合Kumo的应用场景,详细阐述LMDB的性能特征C++ API使用方式以及磁盘存储布局


1. 性能特征

LMDB针对以下场景进行了深度优化:

  • 完全遵循ACID特性的事务处理
  • 内存映射I/O(Memory-mapped I/O)
  • 单写者、多读者(Single-writer, multiple-reader)的并发模型
  • 按键有序遍历

单节点部署(SSD/NVMe存储)下的典型性能指标:

指标典型范围
点查询(Get)~1–20 微秒
顺序扫描受内存带宽限制
写入吞吐量50–300 MB/s(单写者)
读取并发数无限制(多读者)
内存占用主要为映射文件大小 + 页缓存
注意事项

LMDB会将整个数据库映射至内存中。 在Kumo控制平面的使用场景下,每个节点的数据集总量通常不应超过1–2 GB。 超出该阈值可能导致内存压力增大,且写入性能下降。

LMDB最适用于满足以下条件的嵌入式工作负载

  • 要求事务性保障
  • 读操作占比远高于写操作
  • 数据集可完全容纳于虚拟内存中

2. C++ API使用方式

Kumo通过C++封装层调用LMDB的原生C API,核心使用方式如下:

打开环境

#include <lmdb.h>

MDB_env* env;
int rc = mdb_env_create(&env);
mdb_env_set_maxdbs(env, 16);
mdb_env_set_mapsize(env, 1UL * 1024 * 1024 * 1024); // 1 GB
rc = mdb_env_open(env, "/data/kumo/lmdb", 0, 0664);

启动事务

MDB_txn* txn;
rc = mdb_txn_begin(env, nullptr, 0, &txn); // 0 = 读写事务

打开数据库

MDB_dbi dbi;
rc = mdb_dbi_open(txn, nullptr, 0, &dbi); // nullptr = 使用默认数据库

写入(Set)

MDB_val key, data;
key.mv_size = strlen("key1");
key.mv_data = (void*)"key1";
data.mv_size = strlen("value1");
data.mv_data = (void*)"value1";

rc = mdb_put(txn, dbi, &key, &data, 0);

读取(Get)

MDB_val val;
rc = mdb_get(txn, dbi, &key, &val);
if (rc == 0) {
std::string value((char*)val.mv_data, val.mv_size);
}

提交事务

rc = mdb_txn_commit(txn);

中止事务

mdb_txn_abort(txn); // 撤销所有未提交的修改

关闭数据库与环境

mdb_dbi_close(env, dbi);
mdb_env_close(env);

3. 磁盘目录结构

典型的LMDB环境目录结构如下:

/data/kumo/lmdb/
├── data.mdb
├── lock.mdb
文件用途
data.mdb内存映射数据库文件
lock.mdb用于单写者并发控制的锁文件

LMDB采用内存映射文件机制,将所有B+树页面常驻内存。该数据库无需后台压缩操作,可自动扩容,但要求预先定义映射文件大小

应用程序应将LMDB环境视为黑盒,严禁手动编辑目录内的任何文件。


总结

LMDB具备以下核心特性:

  • 嵌入式、内存映射的键值存储接口
  • 完全遵循ACID特性的事务能力
  • 极低的读取延迟与无限制的多读者并发能力
  • 简洁、紧凑的磁盘存储格式

该存储系统非常适配Kumo的事务性本地元数据及小型控制平面数据集的存储需求。