必须用github.com/adrg/xdg获取平台合规路径:windows用%appdata%、macos用~/library/application support、linux用~/.local/share;配置走confighome,运行时数据走datahome,并配合os.mkdirall和atomic.writefile确保安全写入。

选对路径:用 xdg 获取平台合规存储目录
硬编码 ./data.json 或 ~/myapp/config.yaml 在打包后几乎必挂——Windows UAC、macOS App Sandbox、Linux systemd user session 都会拦截非常规路径。必须按平台规范生成路径:%APPDATA%(Windows)、~/Library/Application Support(macOS)、~/.local/share(Linux)。
推荐直接用 github.com/adrg/xdg,它严格遵循 XDG Base Directory Spec,且不依赖外部命令:
- 配置类数据(如用户偏好)走
xdg.ConfigHome() - 运行时数据(如缓存、进度、日志)走
xdg.DataHome() - 首次拼接路径前,务必调用
os.MkdirAll(dir, 0755)创建完整目录树 - 避免在
init()里提前调用xdg.DataHome()——某些容器或 systemd service 启动时环境变量尚未就绪,会返回空或错误
选对格式:json 还是 gob?看是否需要人工干预
encoding/json 和 encoding/gob 不是性能取舍问题,而是使用场景分界线。
- 选
json:用户可能手动编辑(比如改"debug": true)、未来要加 Web 管理页、需跨语言读取、结构体字段常变动。注意所有字段必须首字母大写,并加json:"key"tag,否则反序列化后全是零值 - 选
gob:纯 Go 内部使用、字段稳定、含私有字段、追求体积与速度。但结构体字段名一改(UserName→Username),旧文件就静默丢数据,且无法被其他语言解析 - 链式结构(如
A → *B → []C)必须确保所有参与序列化的字段都是导出字段;若含func、chan、unsafe.Pointer,gob会 panic
写入安全:用 atomic.WriteFile 或临时文件 + rename
直接 os.WriteFile(path, data, 0644) 风险极高——进程崩溃、断电、磁盘满都可能导致文件损坏或内容截断。Go 1.19+ 推荐用 atomic.WriteFile;老版本必须手写原子写入逻辑。
- 先写入
path + ".tmp" - 调用
f.Sync()强制刷盘(这行不能少) - 再用
os.Rename()替换原文件——该操作在大多数文件系统上是原子的 - 别用
ioutil.WriteFile(已弃用),也别省略Sync(),否则 tmp 文件可能只写进 page cache 就返回,实际未落盘
BoltDB 使用陷阱:Update/View 必须严格分离
很多人以为 bolt.Open() 后直接 bucket.Put() 就能落盘,结果重启后全丢。BoltDB 的事务模型非常严格:
- 所有写操作(
Put、Delete)必须包裹在db.Update()回调中 - 所有读操作(
Get、ForEach)必须用db.View()回调 - 混用会阻塞、panic 或静默失败;
db.Update()中调db.View()是允许的,反之不行 - 桶(bucket)不存在时,
tx.CreateBucket()必须在Update()里执行,不能在View()里尝试
最易被忽略的是:BoltDB 不提供“自动创建桶”机制,第一次写前必须显式建桶,且建桶和写数据得在同一个 Update() 事务里完成,否则后续 Put() 会因桶为空而失败。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











