必须用go.etcd.io/bbolt,旧路径github.com/boltdb/bolt已归档失效;导入、初始化均须用bbolt,打开数据库需设0600权限并显式配置Timeout,务必defer db.Close(),读写事务严格分离,桶名和键值必须为[]byte。

别用 github.com/boltdb/bolt,它已归档失效;必须用 go.etcd.io/bbolt,否则编译直接失败。
导入路径和初始化必须用 bbolt
2026 年起所有新项目只能用 go.etcd.io/bbolt —— 旧 import 路径不仅被 Go 模块系统拒绝,go mod tidy 会报 module github.com/boltdb/bolt: not found。安装命令是:go get go.etcd.io/bbolt@latest。
打开数据库时务必设权限掩码 0600,不设等于把数据裸露在文件系统里。超时也建议显式配,避免进程卡死在 bolt.Open() 上:
db, err := bolt.Open("data.db", 0600, &bolt.Options{Timeout: 1 * time.Second})
if err != nil {
log.Fatal(err)
}
defer db.Close()
-
db.Close()必须 defer,BoltDB 对文件加独占锁,不关会导致后续 Open 阻塞甚至整个应用重启失败 - 路径不存在会自动创建,但父目录必须存在,否则
open报no such file or directory - 权限不足、文件被其他进程占用(比如另一个 Go 进程没调
Close)、路径写错,是Open失败三大主因
读写事务必须严格分离:View 和 Update 不能混
db.View() 只能读,db.Update() 才能写——这不是风格建议,是底层锁机制硬约束。同一时刻只允许一个 Update 事务活跃,但可并发多个 View。
立即学习“go语言免费学习笔记(深入)”;
常见错误现象:
- 在
View里调b.Put():静默失败,返回nil,键值根本没存进去 - 在
View里调tx.CreateBucket():直接 panic,错误类似panic: invalid operation on read-only transaction - 把高频查询塞进
Update:写事务排队,QPS 断崖下跌
正确姿势:
- 纯查询走
db.View() - 单次写入用
db.Update() - 批量写入优先考虑
db.Batch(),减少事务开销 -
Update函数体里必须返回nil才提交,返回任意error都自动回滚
CreateBucketIfNotExists 是默认选择,别手写 if 判断
新手常这么写:
if b := tx.Bucket([]byte("users")); b == nil {
tx.CreateBucket([]byte("users"))
}
这段代码在并发下有竞态:两个事务同时判断 bucket 不存在,然后都去 CreateBucket,第二个必然 panic 报 bucket already exists。
tx.CreateBucketIfNotExists([]byte("users")) 内部带原子性检查,安全又简洁。注意:
- 桶名必须是
[]byte,传"users"字符串会编译失败 -
CreateBucket()只适合初始化阶段的单次执行,比如服务启动时一次性建好所有桶 - 返回的
*bbolt.Bucket不能跨事务复用,每个事务都要重新tx.Bucket()
键和值必须是 []byte,序列化绕不过
BoltDB 不认识 int、struct、bool,只认字节切片。你不能写 b.Put([]byte("user1"), &u),Go 编译器会报类型错误;就算强制转成 []byte,存进去的也只是内存地址垃圾。
正确做法是序列化:
- 简单字符串:直接
[]byte("hello") - 整数 key 推荐
binary.BigEndian.PutUint64(),确保字典序 = 数值序 - 结构体推荐
json.Marshal(),读取时json.Unmarshal() - 避免用
fmt.Sprintf拼接 key,性能差且易出错
最后提醒一句:BoltDB 的 .db 文件是二进制 B+ 树结构,无法手动编辑。任何初始化数据、修复或验证,都得靠程序调 API 或用 bbolt 命令行工具(如 bbolt info、bbolt keys)或 boltbrowser 查看。

















