不能直接用 os.WriteFile 写切片或结构体,因为它仅写内存地址快照,重启后读取会乱码或 panic;必须用 encoding/gob 或 encoding/json 序列化,并确保原子写入和目录初始化。

直接用 os.WriteFile 写切片或结构体是错的——它只写内存地址快照,重启后读出来就是乱码或 panic。
为什么不能直接 os.WriteFile("cache.gob", []byte(data), 0644)
Go 的 []int、map[string]*User 等类型不是纯数据块,而是含指针、长度、底层数组地址的运行时结构。强制转 []byte 要么编译失败,要么绕过类型系统写入无效地址。常见现象包括:
-
panic: runtime error: makeslice: cap out of range(读取时尝试用损坏长度重建切片) - 反序列化后切片长度为 0,或字段全为零值
- 同一程序两次运行间
append导致底层数组重分配,旧快照彻底失效
选 encoding/gob 还是 encoding/json?看缓存用途
两者不是性能选择题,而是场景分界线:
- 用
gob:纯 Go 内部缓存快照,含私有字段、需保留 nil 切片/字符串语义、追求体积小速度高。但要求 encode/decode 两端类型定义完全一致(包路径、字段名大小写都不能变) - 用
json:缓存可能被人工检查、未来要加 Web 查看页、需跨语言调试。注意所有字段必须首字母大写,并显式加json:"key"tag;否则反序列化后全是零值
示例中若定义 type Cache struct { Items []string },就不能混用 Items []string `json:"items"` —— gob 完全忽略 tag,字段名大小写必须严格匹配。
立即学习“go语言免费学习笔记(深入)”;
必须做原子写入,否则崩溃即丢快照
缓存快照常在进程退出前或定时保存,此时断电、OOM、磁盘满极易发生。直接 os.WriteFile 写原路径风险极高:
- 写到一半中断 → 文件内容截断,下次加载 panic
- page cache 未刷盘 → 看似写成功,实际数据还在内存里
Go 1.19+ 推荐用 atomic.WriteFile;老版本必须手写:
tmpPath := path + ".tmp" f, _ := os.Create(tmpPath) gob.NewEncoder(f).Encode(data) f.Sync() // 这行不能少 f.Close() os.Rename(tmpPath, path) // 大多数文件系统上原子
路径和目录初始化容易漏掉
缓存文件通常放在 xdg.DataHome() 下(如 ~/.local/share/myapp/cache.gob),但有两个坑:
- 别在
init()里调xdg.DataHome()—— systemd service 或容器启动时环境变量常未就绪,返回空字符串 - 拼出完整路径后,必须先
os.MkdirAll(dir, 0755),否则os.Create会因父目录不存在而失败
真正安全的缓存快照写入,核心不在序列化格式,而在路径准备、原子落盘、错误兜底这三步。字段改名、跨平台读取、进程崩溃恢复——这些才是实际压测时暴露最多的点。


















