lmdb的MDB_MAP_FULL错误
在构建 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; }