badgerdb 使用需严格遵循路径设置、事务提交、值拷贝和关闭四步,缺一不可。必须显式指定 dir 和 valuedir 并提前创建目录;写操作须走事务且务必 commit;读取时必须用 valuecopy;close 仅调一次;还需手动触发 valueloggc。

BadgerDB 不是“引入包就能用”的嵌入式数据库,路径、事务、值拷贝、关闭这四步错任何一环,数据就可能静默丢失或读出乱码。
badger.Open() 必须显式设置 Dir 和 ValueDir
Badger 把索引(key)和值(value)存在两个物理路径,Dir 存 LSM 树结构,ValueDir 存 value log 文件。两者可以相同,但不能省略——否则它会静默用 os.TempDir() 创建临时目录,进程退出后数据全丢,且不报错。
- 错误写法:
badger.Open(badger.DefaultOptions("/data"))——ValueDir落在/tmp,和Dir分离,GC 失败、manifest 加载异常都可能发生 - 正确写法:显式赋值,哪怕路径一样:
opts := badger.DefaultOptions("/data")<br>opts.ValueDir = "/data" - 务必提前创建目录:
os.MkdirAll("/data", 0755),Badger 不建父级目录;用filepath.Abs("./data")转绝对路径,避免工作目录变动导致加载失败
db.Update() 是安全写入口,手写 txn 容易漏 Commit
Badger 没有 Put() 这类直写接口,所有写必须走事务。漏掉 txn.Commit() 不会 panic、不报错,但数据根本没落盘——你查不到,重启也看不到。
- 高频单条写,优先用
db.Update():err := db.Update(func(txn *badger.Txn) error {<br> return txn.Set([]byte("k"), []byte("v"))<br>}) - 手写事务时,必须配
defer txn.Discard()做兜底,成功后再txn.Commit()替换掉它 - 千万别在
db.View()里调db.Update(),会触发"recursive txn"panic - 批量写用
WriteBatch:wb := db.NewWriteBatch(); wb.Set(k, v, 0); wb.Flush(),自动管理事务,不暴露txn对象
Item.ValueCopy(nil) 不是可选项,是强制操作
Item.Value() 返回的是事务内共享内存切片,事务一结束(比如 db.View() 函数返回后),这块内存可能已被复用或覆盖。直接用 string(item.Value()) 或存进 map,大概率读到空、乱码,甚至 panic。
- 安全读法:
val, err := item.ValueCopy(nil),nil表示让 Badger 自动分配新内存 - 结构体反序列化前必须先
ValueCopy:json.Unmarshal(val, &user),别传item.Value()直接进去 - 迭代器遍历时,每次
iter.Next()后都要重新ValueCopy,否则所有 value 都显示为最后一次的值(底层缓冲被反复覆盖)
db.Close() 必须且仅调用一次,不关或重复关都会损坏文件
Badger 用 mmap 和后台 goroutine 管理文件,不调 db.Close() 就退出进程,value log 文件会损坏,下次启动直接报 "manifest has unsupported version" 或 "checksum mismatch"——这不是配置问题,是文件已坏。
- 务必在程序退出前调用,例如
main()结束前、或捕获SIGINT后 - 重复调用
db.Close()会 panic,所以不要在 defer 里多次注册,也不要在多个 goroutine 里并发调 - 如果用
defer db.Close(),确保它只注册一次;若需手动控制生命周期(如 Web 服务热重启),请加锁保护
最常被忽略的是 value log GC:Badger 不自动清理已删除 value 占用的空间,磁盘只增不减。上线后第一件事就是加定时任务调 db.RunValueLogGC(0.7),并设好 WithValueThreshold(32) 避免小 value 全进 log。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











