BadgerDB 使用必须显式设置 Dir 和 ValueDir,写操作需开启事务并 Commit,读取须用 ValueCopy 获取独立副本,服务退出前必须调用 Close;否则将导致数据丢失、panic 或文件损坏。

打开数据库时 Dir 和 ValueDir 必须显式设置
BadgerDB 不会帮你“猜”路径。只传一个目录、或传空字符串,badger.Open() 会静默创建临时目录,进程一退出数据就丢——你根本不会收到报错,但数据已消失。
- 正确做法:必须同时指定
Dir和ValueDir,哪怕它们是同一路径:opts := badger.DefaultOptions("/data/badger")<br>opts.ValueDir = "/data/badger" - 错误写法:
badger.Open(badger.DefaultOptions("/data/badger"))(没设ValueDir)或badger.Open(badger.DefaultOptions(""))(空路径) - 微服务部署时注意:K8s Pod 重启、Docker 容器重建后,若没挂载持久卷,/tmp 或 /var/tmp 下的路径等于白配
写操作必须用 true 开启事务,且 Commit 不可省略
Badger 没有裸 Put 接口。漏掉 true 参数,事务就是只读的,调 Set 直接 panic:"cannot perform write on read-only transaction";忘了 Commit(),数据压根不落盘,还无任何提示。
- 安全写法(推荐用
Update封装):err := db.Update(func(txn *badger.Txn) error {<br> return txn.Set([]byte("user:1001"), []byte(`{"name":"alice"}`))<br>}) - 手动事务需配
defer txn.Discard()防泄漏,再加if err != nil { return }提前退出,避免误Commit - 高频写场景下,
SyncWrites = true建议开启,否则进程崩溃可能丢最近一批未刷盘数据
读取值必须调用 ValueCopy,不能直接用 Value()
Item.Value() 返回的是事务内共享内存切片,事务一结束,这块内存可能被复用或释放。后续访问轻则读到脏数据,重则 panic。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 正确读法:
item, err := txn.Get([]byte("user:1001"))<br>if err == nil {<br> val, err := item.ValueCopy(nil)<br> if err == nil {<br> // val 是独立字节切片,可安全传递、序列化、跨 goroutine 使用<br> }<br>} -
ValueCopy(nil)内部自动分配内存,不用预估长度;传已有切片(如make([]byte, 0, 1024))可复用底层数组,但要注意容量是否够用 - 常见坑:在
txn.Commit()后还拿着item.Value()去json.Unmarshal,偶尔成功、偶尔 crash,极难复现
服务退出前必须 Close,且不能重复调用
Badger 用 mmap 和后台 goroutine 管理文件。不 Close() 就退出进程,value log 文件大概率损坏,下次启动报错:"manifest has unsupported version" 或 "checksum mismatch";重复 Close() 会 panic。
立即学习“go语言免费学习笔记(深入)”;
- 务必在 main 函数结尾、或 signal handler 中调用:
defer db.Close() // 最保险的位置
- K8s 场景下要捕获
SIGTERM,给db.Close()留出几秒时间(默认超时 5s,可通过WithTableLoadingMode等参数调优) - 别在 HTTP handler 里开/关 DB —— 这不是连接池,整个服务应共用一个
*badger.DB实例
ValueCopy 的必要性和 Close 的强制性。这两处不按规范做,问题不会立刻暴露,而是在高并发、服务重启、或长时间运行后突然爆发。


















