BoltDB读写必须分离:写用db.Update(),读用db.View(),因底层锁机制限制;View中写操作静默失败或panic,Update返回error则回滚;并发创建bucket须用CreateBucketIfNotExists()避免竞态;键值必须为[]byte,需序列化;小整数key推荐binary.BigEndian确保字典序=数值序;Open失败主因是文件占用、权限不足或路径不存在。

db.Update() 和 db.View() 别混用场景
写数据必须用 db.Update(),读数据必须用 db.View() —— 这不是风格问题,是底层锁机制决定的。BoltDB 同一时刻只允许一个读写事务(Update),但允许多个并发只读事务(View)。如果在 View 里调 b.Put(),程序不会报错,但会静默失败,返回 nil,而键值根本没存进去。
-
Update内部必须返回nil才提交;返回任何 error 都会自动回滚 -
View中尝试写操作(如b.Put、tx.CreateBucket)会直接 panic,错误信息类似panic: tx closed或invalid operation on read-only transaction - 高频读+低频写的 Web 服务,把查询全塞进
Update会导致写事务排队阻塞,QPS 断崖下跌
CreateBucketIfNotExists() 是默认选择,别手写 if 判断
新手常这么写:if b := tx.Bucket([]byte("users")); b == nil { tx.CreateBucket(...) } —— 这段逻辑在并发下有竞态:两个事务同时判断 bucket 不存在,然后都去创建,第二个会因 bucket 已存在而报 bucket already exists 错误。
- 直接用
tx.CreateBucketIfNotExists([]byte("users")),它内部带原子性检查,安全又简洁 -
CreateBucket()只在确定 bucket 绝对不存在时才用(比如初始化阶段单次执行) - bucket 名必须是
[]byte,传"users"字符串会编译失败;传string([]byte("users"))也没用,类型不匹配
键和值都必须是 []byte,别想绕过序列化
BoltDB 不认识 struct、int、bool —— 它只认字节切片。你不能直接 b.Put([]byte("user1"), &User{...}),Go 会报类型错误;即使强制转换,存进去的也是内存地址垃圾。
- 推荐用
encoding/json:先json.Marshal(u)得到[]byte,再存;读出来后json.Unmarshal(data, &u) - 小整数当 key?别用
strconv.Itoa(123),用binary.BigEndian.PutUint64(buf, uint64(123))—— 这样能保证字典序 = 数值序,方便范围查询 - 值为空(
nil)时,b.Get()返回nil,不是空 slice;别用len(v) == 0判空,要用v == nil
Open() 失败常见原因就三个,按顺序排查
运行时报 timeout acquiring lock 或卡住不动,基本不是代码问题,而是文件权限或进程冲突。
立即学习“go语言免费学习笔记(深入)”;
- 数据库文件被另一个进程(包括未退出的调试器、上一次 panic 的残留进程)占着 —— 用
lsof my.db(macOS/Linux)或handle my.db(Windows Sysinternals)查谁在用 - 权限不对:Linux/macOS 下
0600意味着只有文件所有者可读写,如果用sudo go run创建,后续普通用户运行就会 Permission Denied - 路径不存在且上级目录不可写:比如
./data/my.db,但./data目录还没mkdir,Open 会失败,错误信息是no such file or directory,不是“找不到数据库”而是“找不到路径”


















