lmdb报permissionerror主因是锁文件权限不足或路径只读,非数据库损坏;map_size需预估最大空间并预留余量,过小致mapfullerror;事务须显式commit才可见;多进程需统一map_size且避免重复close。

为什么 lmdb 打开环境时总报 PermissionError: Permission denied
常见于 Windows 或容器内运行时,lmdb 默认创建的锁文件(lock.mdb)需要写权限,但当前用户没权限或路径被只读挂载。不是数据库损坏,也不是文件被占用——是锁机制卡在第一步。
- Linux/macOS 下确保目标目录可写:
chmod 755 /path/to/env,别用root创建后切普通用户跑 - Windows 上避免放在
C:\Program Files或受 UAC 保护路径,改用用户目录如%USERPROFILE%\lmdb-data - 容器中若挂载只读卷,必须显式加
map_size并设read_only=True,否则初始化仍会尝试写锁文件 - 临时调试可用
subdir=False参数绕过子目录结构(不推荐生产),它把数据直接写进指定文件而非目录
lmdb 的 map_size 设太小会导致 MapFullError,怎么预估
map_size 是内存映射区总大小,不是当前数据量,而是“未来可能用到的最大空间”。它一旦设定就不能动态扩大(除非关库、扩容、重开),且必须大于当前所有数据+页开销。设小了不是慢,是直接崩溃。
- 估算公式:预期键值对总数 × (平均键长 + 平均值长 + 64 字节页头开销),再乘 1.3~2 倍余量
- 如果键值都是短字符串(如
user:123→{"score": 95}),100 万条建议从1024 * 1024 * 128(128MB)起步 - 存大 blob(如图片二进制)时,
map_size必须 ≥ 所有 blob 总大小 + 索引开销,否则第一次插入超大 value 就失败 - 开发期可先设大点(如
2**32= 4GB),上线前用env.stat()查psize和last_pgno反推真实占用,再收紧
Python 中用 transaction 写入时为什么查不到刚写的数据
因为 lmdb 的事务默认不自动提交,且每个 transaction 是隔离的。你在一个 txn 里 put 了,不 commit(),别的事务(包括同一个 env 的新 txn)就看不到——这不是缓存延迟,是 MVCC 隔离保证。
- 务必显式调用
txn.commit(),不要依赖上下文管理器自动退出(虽然with env.begin(write=True)会 commit,但异常时会 abort,得确认逻辑路径) - 读操作也必须在
begin()后进行,不能直接用env.get(key)—— 这会隐式建一个只读事务,但无法看到未 commit 的写入 - 高并发写场景下,避免长事务:大循环里逐条
put后统一commit,比每条都开新事务快十倍以上,但要小心事务超时(默认 1 小时) - 如果只是想“立刻可见”,检查是否误用了
write=False的事务,或者开了多个Environment实例指向同一路径(这是未定义行为)
lmdb 在多进程下共享数据要注意什么
lmdb 原生支持多进程读写,但前提是所有进程共用同一个 Environment 实例(即同一路径),且不能各自 open 多次——每个进程一个 env 对象没问题,但每个 env 必须指向同一物理路径,并复用 map_size 等参数。
立即学习“Python免费学习笔记(深入)”;
- 父进程 open 后 fork 出的子进程,可直接复用该
env对象(Unix 下安全),但 Windows 不支持 fork,必须各进程独立open - 所有进程必须用完全一致的
map_size值,否则后打开的进程会拒绝加载(报Invalid argument) - 避免在多进程里频繁
env.close(),尤其是子进程 exit 前——可能导致锁文件残留,下次 open 失败 - 删除数据不用
del,要用txn.delete(key);清空整个 db 用txn.drop(db, delete=True),别手动删文件
事情说清了就结束。最常出问题的其实是 map_size 和权限,而这俩又没法靠 traceback 直接定位——报错信息太泛,得结合场景倒推。










