badgerdb优于boltdb和leveldb,因其纯go实现、ssd优化的lsm-tree+value log设计,在高并发小key场景下写吞吐更高、读延迟更低;但需显式配置dir/valuedir、syncwrites和numversionstokeep,并注意事务中valuecopy及关闭前正确调用close()。

为什么选 BadgerDB 而不是 Bolt 或 LevelDB?
BadgerDB 在写吞吐和读延迟上明显优于 Bolt(纯 Go 实现但基于 mmap + 事务锁),也比 LevelDB(Cgo 绑定)更易部署——它不依赖 CGO,编译产物干净,适合容器化或嵌入式场景。但它的 LSM + Value Log 架构意味着 ValueLogFileSize 和 MaxTableSize 配置不当会导致磁盘暴涨或 GC 延迟飙升。
初始化时必须设置的三个关键选项
Badger 的 Options 结构体里,很多字段有默认值,但生产环境必须显式覆盖:
-
Dir和ValueDir必须指向不同路径(哪怕只是软链接),否则ValueLog写入会和MANIFEST争抢 fsync,引发 write stall -
SyncWrites默认为false,若业务要求强持久化(如金融类日志),需设为true,但会显著降低写吞吐 -
NumVersionsToKeep默认是 1,如果用GetOrSet类操作频繁更新同一 key,旧版本 value 不会被及时清理,ValueLog文件只增不减
示例初始化:
opt := badger.DefaultOptions("/data/badger").
WithValueDir("/data/badger-value").
WithSyncWrites(true).
WithNumVersionsToKeep(2)
事务中如何安全地 Get + Set + Delete
Badger 的事务不是“自动提交”,所有操作都需在 txn.Commit() 后才真正生效;且 txn.Get() 返回的是只读句柄,必须用 item.ValueCopy(nil) 拷贝数据,否则底层内存可能被后续 GC 回收。
- 避免在事务外调用
item.Value()—— 它返回的是内部 buffer 引用,事务结束后内容不可靠 -
txn.Delete()和txn.Set()可混合调用,但重复Set同一 key 会覆盖前一次,不是追加 - 若需原子性读-改-写(如计数器),必须用
txn.Get() → item.ValueCopy() → txn.Set()流程,不能拆成两个事务
典型误用:
val, _ := item.Value() // 危险!指针可能失效 txn.Set(key, val) // 可能 panic 或写入垃圾数据
关闭数据库前必须做两件事
Badger 不支持“热重启”,db.Close() 会阻塞直到所有 pending writes flush 完、value log compact 完成。若直接 kill 进程,下次启动可能触发 long recovery(尤其 value log 大于 1GB 时)。
- 务必在
os.Interrupt信号处理中先调用db.Stop()(非必须但可加速 shutdown) - 然后调用
db.Close(),并设超时(建议 ≥30s),避免进程僵死 - 若应用频繁启停,建议启用
WithTruncate(true),让Close()自动截断未 compact 的 value log 尾部(牺牲少量数据换取快速退出)
容易忽略的是:即使没显式写入,Badger 内部也会定期触发 value log GC;Close() 会等待该 GC 完成——所以小数据量下关闭快,大数据量下可能卡住十几秒。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











