在构建 lmdb 数据集时,如果 map_size 设置的不够大,就会出现 MDB_MAP_FULL 错误。lmdb 没有提供自动扩展机制,因此当写入的数据大于 map_size 时,就会出现 MDB_MAP_FULL 错误。

对于这个问题的解决方案网上一般建议给一个足够大的 map_size。这个方案的好处是,简单且不影响写的性能。缺点是不好把握 map_size 的大小,设的太大会导致生成的文件也很大,浪费不必要的磁盘空间。

这里给的方案是动态调整 map_size。好处是生成的文件大小恰当。缺点是代码相对复杂,会影响写的性能,影响其实也不大。

注意 commit 产生的 MDB_MAP_FULL 不能调用 abort,因为 commit 失败时会调用 abort,再调用 abort 会出异常。

Python 示例:

key = ...
data = ...
while True:
is_committing = False
try:
txn = env.open(write=True)
txn.put(key, data)
is_committing = True
txn.commit()
except lmdb.MapFullError:
if not is_committing:
txn.abort()
env_info = self._env.info()
map_size = env_info['map_size']
map_size += len(key) + len(data) + 128
env.set_mapsize(map_size=map_size)
continue
break

C++ 示例:

MDB_val key;
...
MDB_val data;
...
while (true) {
MDB_txn* txn;
Check (mdb_txn_begin (env, nullptr, 0, &txn));
MDB_dbi dbi;
Check (mdb_dbi_open (txn, nullptr, 0, &dbi));
int code = mdb_put (txn, dbi, &key, &data, 0);
if (code == MDB_MAP_FULL) {
mdb_txn_abort (txn);
MDB_envinfo stat;
Check (mdb_env_info (env, &stat));
const size_t newSize = key.mv_size + data.mv_size + 128;
Check (mdb_env_set_mapsize (env, newSize));
continue;
}
code = mdb_txn_commit (txn);
if (code == MDB_MAP_FULL) {
// 注意,这里不能 abort;否则会出异常。因为 commit 失时就已经调用了 abort。
MDB_envinfo stat;
Check (mdb_env_info (env, &stat));
const size_t newSize = key.mv_size + data.mv_size + 128;
Check (mdb_env_set_mapsize (env, newSize));
continue;
}
break;
}

  1. Resizing of LMDB map

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注