bufio.writer 必须显式 flush 才能落盘,否则数据仅在内存缓冲区;defer flush 在 panic 时失效;缓冲区大小需按场景调整;并发写应通过 channel 串行化;flush 后是否 sync 取决于数据重要性;禁止混用 file.write 和 w.write。

bufio.Writer 忘记 Flush 就等于没写
数据调用 w.Write() 后只是进了内存缓冲区,不调 w.Flush() 永远不会落盘。常见现象是:程序跑完、文件存在、但内容为空或只有一半;panic 退出时未执行 defer w.Flush(),数据直接丢失。
-
defer w.Flush()只在函数正常返回时触发,若中间return或 panic,可能跳过 - 真正写磁盘的错误(如磁盘满、权限不足)只在
Flush()时暴露,Write()几乎总返回nil - 长周期服务(如日志 daemon)不能只靠结尾一次
Flush(),需配合定时或批量策略
缓冲区大小不是默认就好,得看写入模式
默认 4KB 缓冲区适合小日志行或低频写入,但对大文件导出、高吞吐日志等场景明显不够——太小导致频繁刷盘,性能上不去;太大又浪费内存、增加延迟。
- 日志追加写(每行 ~100B):推荐
32*1024(32KB)到64*1024(64KB) - 批量导出(JSON/CSV 行较大或单次写 >1KB):可设为
256*1024~1024*1024(256KB–1MB) - 内存受限或嵌入式环境:保持默认或设为
8*1024,避免 GC 压力突增 - 设置方式:
w := bufio.NewWriterSize(file, 64*1024)
并发写同一个文件?别直接上锁,改用 channel + 单 writer
多个 goroutine 直接共用一个 bufio.Writer 会 panic;加 sync.Mutex 保护写入又容易成为瓶颈。正确做法是让写入逻辑串行化,由单一 goroutine 消费 channel 数据。
- 生产者把日志/数据发到带缓冲的
chan string(如make(chan string, 1000)) - 消费者 goroutine 内部持有一个
bufio.Writer,持续rangechannel 并批量写入 - 必须在 consumer 退出前显式
w.Flush(),否则最后几条日志丢失 - 不要在 consumer 里
defer w.Flush(),因为range不会自动结束,需监听donechannel 主动退出
Flush 之后还要不要 Sync?看数据是否“丢不起”
w.Flush() 只保证数据从 Go 缓冲区写到内核页缓存,不保证已落物理磁盘。file.Sync() 才强制刷盘,但代价高——每次调用都触发一次 fsync 系统调用,吞吐量可能骤降 5–10 倍。
- 普通日志、临时输出:不需要
Sync(),Flush()足够 - 关键状态标记(如“任务已完成”)、金融类确认文件:应在
Flush()成功后立即跟file.Sync() - Linux 下慎用
O_DIRECT:需内存对齐、块大小对齐,且绕过页缓存后错误处理更复杂,99% 的业务没必要碰
bufio.Writer 和底层 *os.File 是强绑定的,不能混用 file.Write() 和 w.Write() 写同一个文件句柄——会导致数据错乱甚至 panic**。一旦包了 bufio.Writer,所有写操作必须走它。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











