BadgerDB生产使用需显式设置Dir和ValueDir、写操作必须用true参数开启事务、读取值须调用ValueCopy、关闭前必须调用Close(),否则易致数据丢失、panic或文件损坏。

BadgerDB 不是“开箱即用就稳”的嵌入式 KV,初始化错、事务漏提交、value log 膨胀失控,三者任一都会让服务在半夜报警。
badger.Open() panic: “manifest has unsupported version” 怎么办
这是 v1 升级到 v2/v3 后最典型的兼容性断裂——新版 Badger 直接拒绝读取 v1 的 MANIFEST 文件,不报错提示,只 panic。
- 先确认你实际依赖的版本:
go list -m github.com/dgraph-io/badger/v3(v3 是当前主线,v2 已归档) - 绝对不要把 v1 的数据目录(比如
./badger)直接传给 v3 的badger.Open() - 迁移必须走二进制导出/导入:用 v1 的
badger backup --dir ./v1-data --backup-dir ./backup,再用 v3 的badger restore --dir ./v3-data --backup-dir ./backup - 新项目初始化时显式写全路径:
Options{Dir: "./data", ValueDir: "./data"},避免不同平台对默认ValueDir解析不一致
Update 事务卡住、CPU 空转、后续请求全挂起
这不是锁竞争,而是事务没结束——txn.Commit() 或 txn.Discard() 漏调用,导致 pendingWrites 队列堆积、memtable 不释放、写锁长期持有。
- 所有
db.Update()必须确保函数体末尾有明确返回,且内部无 panic 漏洞;建议用defer包一层txn.Discard()做兜底 - 别在
Update函数里做任何耗时操作:HTTP 请求、JSON 解析、循环计算都得挪到事务外 - 批量写优先用
WriteBatch:wb.Set(key, val, 0)多次后wb.Flush(),它自动管理事务生命周期,不暴露txn对象 - 监控
db.Stats().WriteStall,为true说明 LSM 正在 compaction,此时Update会排队,不是代码问题
磁盘空间只增不减,value.log 涨满但 df -h 显示已用 98%
Badger 的 value 分离设计决定了:删除 key 只是标记 value 为垃圾,不会立即回收磁盘——GC 必须手动触发,且默认关闭。
立即学习“go语言免费学习笔记(深入)”;
- 上线后第一件事就是加定时任务:
db.RunValueLogGC(0.7)(当 70% value 空间无效时触发),注意该操作会阻塞写入,安排在凌晨低峰期 - 调小
ValueLogFileSize:默认 1GB,设为256 * 1024 * 1024可加快单次 GC 效率,代价是文件数略增 - 检查是否误设了
WithValueThreshold(0):这会让所有 value 进 log,应设为合理阈值,如32(32 字节以上才进 value log) - 大 value(>1KB)别硬塞 Badger:存本地文件或对象存储,Badger 里只存路径或 SHA256,否则 GC 压力指数上升
Get 返回 ErrKeyNotFound,但 Iterator 却能扫出这个 key
这是 value log GC 和索引不同步的典型症状:key 还在 LSM 中,对应 value 却已被 GC 清掉,查询时找不到 value 就直接报错,不回退查旧版本。
- 根本原因是
NumVersionsToKeep设太低(默认 1),又没及时跑 GC;高频写 + 低频 GC 容易积累“悬空指针” - 若业务允许轻微延迟可见性,可临时提高
NumVersionsToKeep到 3~5,缓解 GC 压力,但要同步加大磁盘预算 - 更稳妥的做法是:写入前预估 value 大小,对 >64B 的值启用 TTL,并配合定期
RunValueLogGC,不让垃圾堆积超过两轮 compaction 周期 - 别依赖 Iterator 扫描结果做最终一致性判断——它读的是 LSM 当前状态,不含 value 存活校验
Badger 的“高性能”建立在你主动管理 value 生命周期、理解 GC 触发边界、并接受事务显式语义的前提下;把 LSM 当成黑盒用,不出三天就会遇到磁盘告警和事务卡死。

















