bufio.Writer 必须显式 Flush 才能写入数据;缓冲区大小需按写入粒度调整,高频小写建议设为 64KB~256KB;并发写应通过 channel 由单 goroutine 消费并刷盘,避免共享 Writer 或混用 Write 方法。

bufio.Writer 必须显式 Flush,否则数据只在内存里;缓冲区大小不是默认 4KB 就行,得按写入粒度调;并发写同一文件不能靠 sync.Mutex 硬锁,容易错位或性能崩。
为什么批量写入变慢:系统调用和缓冲区失配
高频小写(比如每条日志 80 字节)用默认 4KB 缓冲区,相当于每 50 条就触发一次 write 系统调用—— syscall 开销远超数据本身。实测吞吐可能卡在几百 KB/s,strace 会看到大量阻塞的 write 调用。
- 避免直接用
os.File.Write循环写,它不缓冲,每次都是 syscall -
bufio.NewWriter(f)默认 4KB 缓冲区仅适合低频写;批量场景建议设为64 * 1024~256 * 1024 - 写入前检查
w.Available(),防止WriteString触发底层切片扩容(copy 开销) - 不要混用
f.Write和w.Write,底层文件偏移量会错乱
如何安全地高并发写同一个文件
多个 goroutine 共享一个 bufio.Writer 会 panic;加 sync.Mutex 锁 Write 又无法解决内核偏移量竞争,短内容下仍可能覆盖。
- 推荐方案:用
chan []byte或chan string做生产者-消费者,单个 goroutine 持有bufio.Writer消费并批量刷盘 - 若必须多 goroutine 写,每个都用独立
os.OpenFile(..., os.O_APPEND, ...)+ 独立bufio.Writer - 绝对不要让多个 goroutine 调用同一个
w.Flush()—— 它不是线程安全的 - 消费者退出前必须显式
w.Flush(),defer在range ch里无效
Flush 之后要不要 Sync?看丢不丢得起
w.Flush() 只把数据从 Go 缓冲区推到内核页缓存,不保证落物理磁盘。真正的持久化要靠 f.Sync(),但它代价极高——每次触发 fsync,吞吐常跌 5–10 倍。
立即学习“go语言免费学习笔记(深入)”;
- 普通日志、临时导出:只
Flush就够了 - 关键状态文件(如 “任务已完成” 标记)、金融类确认文件:必须在
Flush()成功后立刻跟f.Sync() - Linux 下慎用
O_DIRECT:需内存对齐、块对齐,且绕过页缓存后错误处理更复杂,99% 的业务没必要碰 -
file.Sync()失败意味着磁盘已不可写(满、只读、硬件故障),必须记录并告警
最容易被忽略的是:所有 Flush 和 Sync 都要检查返回值;os.WriteFile 这类封装函数掩盖了中间错误,而真正写磁盘的失败只在 Flush 或 Sync 时暴露。


















