在构建 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

发表回复

您的电子邮箱地址不会被公开。