lmdb在c#中需通过p/invoke或封装库(如lmdb.net)调用,必须显式dispose资源、使用绝对路径、预设mapsize并避免多写环境并发打开同一目录。

LMDB 在 C# 中没有原生支持,必须用 P/Invoke 封装或选封装好的库
LMDB 是 C 写的嵌入式 KV 数据库,C# 不能直接 new 一个 LmdbDatabase——它压根不存在。你得要么自己写 DllImport 调 lmdb.dll,要么用现成封装,比如 Lmdb.NET 或 Unosql.LMDB。前者更活跃、文档稍多,后者更轻但更新慢。别碰那些只有一两个 commit 的“个人封装”,Environment.Exit(0) 都可能触发内存泄漏。
常见错误现象:DllNotFoundException: lmdb.dll(没把 DLL 放对位置)、AccessViolationException(指针传错、环境未初始化或已关闭后还访问)。
- Windows 下确保
lmdb.dll和你的.exe同目录,或在PATH中;Linux/macOS 对应liblmdb.so/liblmdb.dylib - 用
Lmdb.NET时,必须先调NativeMethods.mdb_version(out _, out _, out _)检查是否加载成功,别等Env.Create()才崩 - 所有
Env、Dbi、Txn对象都必须显式Dispose(),它们背后是 unmanaged 内存和文件锁,GC 不管
Env.Open() 失败多半是路径、权限或环境复用问题
Env.Open() 是第一个拦路虎。它不报“路径不存在”,而是直接 MDB_INVALID 或 MDB_PANIC——尤其在 Windows 上,路径含中文、空格、符号,或父目录没写入权限时静默失败。
使用场景:首次创建数据库、多进程共享读、单进程多线程读写混用。LMDB 要求数据目录由单一进程创建并持有 mmap 区域,其他进程只能只读打开(除非用 MDB_NOSUBDIR )。
- 路径必须是绝对路径,
@"C:\data\mydb"安全,"./db"很可能因工作目录不确定而失败 - 首次调用
Env.Open()前,确保目录存在且当前用户有读写权限(Directory.CreateDirectory(path)不够,还得DirectoryInfo.CreateSubdirectory()+ 显式设 ACL) - 不要在多个
Env实例间复用同一个路径,LMDB 不允许多个写环境并发打开同一 DB 目录 - 若需多进程读,主进程用
EnvFlags.MDB_NOMETASYNC | MDB_NOSYNC提升性能,但从进程必须加MDB_RDONLY
Transaction.Commit() 失败通常因为 key/value 超长、txn 超时或磁盘满
LMDB 对单 key/value 有硬限制:key 最大 511 字节(默认页大小 4KB),value 理论上可达 2GB,但实际受 Env.SetMapSize() 控制。超了不报“太长”,而是 MDB_BAD_VALSIZE 或直接 Commit() 返回 false。
性能影响明显:频繁小 Commit() 会拖慢吞吐;大事务(如一次写 10 万条)可能触发 MDB_MAP_FULL——不是数据满了,是内存映射空间不够,得提前 Env.SetMapSize() 扩容。
- 写前检查
key.Length ,<code>value.Length别超过当前MapSize的 1/10(留余量) - 批量写用
WriteBatch(Lmdb.NET不自带,得手写循环 + 单Txn);每 1000–5000 条Commit()一次,别攒到内存爆 -
Env.SetMapSize()必须在Env.Open()之后、任何Txn创建之前调,且不能缩小,只能增大 - Linux 下注意
/dev/shm大小(tmpfs),LMDB 默认用它做 lockfile,空间不足会导致MDB_UNABLE_EXTEND_MAPSIZE
字符串 key/value 编码必须统一,否则查不到也无提示
LMDB 存的是字节流,不认字符串编码。你用 Encoding.UTF8.GetBytes("hello") 存,就得用同样方式取;若存时用 UTF8,取时用 ASCII 解,结果就是乱码——但不会抛异常,GetString() 只是按错码表硬转,键匹配直接失败。
容易被忽略的地方:空字符串、null 值、BOM 头。LMDB 允许 key 为 null(即 IntPtr.Zero),但 .NET 封装库一般不暴露这个接口,强行传 null 很可能 NRE。
- 约定全部用
Encoding.UTF8,并在工具方法里封装:如ToKey(string s) => Encoding.UTF8.GetBytes(s),避免散落各处 - key 避免以
\0开头或含\0,C 层会截断;value 含\0没问题,LMDB 不当字符串处理 - 调试时用
unsafe+fixed把 value 字节数组打出来看十六进制,比猜编码靠谱得多 - 如果要用复合 key(如 user_id+timestamp),别拼字符串,用
BitConverter写入定长二进制,省空间也避编码歧义
事情说清了就结束










