os.openfile追加写入必须同时指定os.o_append、os.o_wronly、os.o_create三者缺一不可;漏任一flag将导致覆盖、报错或无效;禁用os.writefile追加;应复用*os.file与bufio.writer并加锁保障并发安全。

os.OpenFile 的 flag 组合必须三者齐全
只写 os.O_APPEND 会报 invalid argument;漏掉 os.O_CREATE,文件不存在时直接返回 no such file or directory;缺 os.O_APPEND 则从头覆盖——这三个 flag 缺一不可:
-
os.O_APPEND:确保每次Write都原子定位到末尾(内核级,不依赖当前偏移) -
os.O_WRONLY:必须配合O_APPEND使用,O_APPEND | O_RDONLY无效 -
os.O_CREATE:文件不存在时自动创建,权限参数0644仅在此刻生效
错误示例:os.OpenFile("log.txt", os.O_WRONLY|os.O_CREATE, 0644) —— 没 O_APPEND,新内容从头写入,旧数据残留后半段,大文件里极难排查。
别用 os.WriteFile 做追加
os.WriteFile 内部固定调用 os.Create,等价于 O_CREATE | O_WRONLY | O_TRUNC。哪怕你先读、再拼、再写,中间存在竞态窗口:
- A goroutine 读取 100 行 → B goroutine 追加第 101 行 → A 全量写回 → B 的内容被彻底覆盖
- 小文件凑合,多 goroutine 或多进程下必丢数据
- 性能差:每次都要把整个文件 load 到内存再 syscall
它只适合「一次性写入完整快照」,比如保存配置、导出 JSON 报告。
高频追加要复用 *os.File 和 bufio.Writer
每秒写几十次日志时,反复 OpenFile → Write → Close 开销明显。推荐长期持有句柄:
- 用
os.OpenFile打开一次,defer f.Close()放在程序退出前(不是每次写完就关) - 加
bufio.NewWriterSize(f, 65536)缓冲写入,减少系统调用次数 - 必须显式调用
writer.Flush()——defer writer.Flush()不够,panic 或kill -9时缓存内容直接丢失 - 禁止混用:
writer.Write()和f.WriteString()共享底层 fd,字节序错乱,日志可能半行粘连
并发写同一文件末尾仍需加锁
O_APPEND 保证单次 write() 系统调用原子落盘,但 WriteString 不是原子操作:它先拷贝字符串到 runtime buffer,再 syscall。多个 goroutine 同时调用,buffer 写入可能交错:
- 两行日志 “A\n” 和 “B\n” 可能变成 “A\nB\n”、“AB\n\n” 或截断
- 安全做法:用
sync.Mutex包一层写逻辑,或改用log.New(f, "")(内部已加锁) - 更轻量方案:用 channel 聚合日志,单个 goroutine 顺序写入
Windows 下换行符和权限容易被忽略:\n 在记事本里显示为一行,建议统一用 \r\n;0644 在 Windows 上基本无效,但首次创建时若父目录无写权限,会静默失败。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











