直接用 os.File.Write 写小数据慢是因为每次调用都可能触发系统调用,开销大;应使用 bufio.Writer 缓冲攒批写入,并确保 Write/Flush/Close 全流程错误处理与及时刷新,且 bufio.Writer 非并发安全。

为什么直接用 os.File.Write 写小数据很慢
每次调用 os.File.Write 都可能触发一次系统调用,尤其在循环中反复写入短字符串(如日志行、JSON片段)时,I/O 开销会急剧放大。这不是 Go 的问题,而是底层 write(2) 系统调用本身的开销。缓冲的本质是攒一批数据,一次性刷到内核,减少上下文切换次数。
bufio.Writer 是标准解法,但要注意刷新时机
bufio.Writer 封装了内存缓冲区,写入操作先落在用户态 buffer,直到缓冲区满、显式调用 Flush() 或 writer 关闭时才真正落盘。常见误用是只写不刷:
file, _ := os.Create("out.txt")
w := bufio.NewWriter(file)
w.WriteString("hello")
// 忘记 Flush() → "hello" 可能永远不会写入文件
- 务必在写完后调用
w.Flush(),否则数据滞留在内存中 - 用
defer w.Flush()不安全——如果 writer 未被完全使用就退出,可能丢数据;更稳妥的是写完立刻Flush(),再Close()底层*os.File - 缓冲区大小默认是
4096字节,可通过bufio.NewWriterSize(file, 64*1024)调大,适合批量写场景
封装成可复用结构体时,别漏掉错误传播
把 bufio.Writer 包一层本身很简单,但关键在错误处理:写入失败可能发生在 WriteString()(buffer 未满)、Flush()(buffer 满或手动刷)甚至 Close()(隐式 flush + close file)阶段。不能只检查某一步的 error。
type BufferedFileWriter struct {
w *bufio.Writer
f *os.File
}
func (bfw *BufferedFileWriter) Write(p []byte) (int, error) {
return bfw.w.Write(p) // 错误可能来自 buffer 写入(比如已关闭)
}
func (bfw *BufferedFileWriter) Close() error {
if err := bfw.w.Flush(); err != nil { // 必须先 flush
return err
}
return bfw.f.Close() // 再关文件
}
- 所有写方法(
Write、WriteString、WriteByte)都应返回error,且不能忽略bufio.Writer自身可能返回的错误 -
Close()必须包含Flush(),否则等价于丢数据 - 不要在构造函数里做
os.OpenFile失败重试——交给调用方处理更清晰
并发写同一个 bufio.Writer 会崩溃
bufio.Writer 不是并发安全的。多个 goroutine 同时调用它的 Write 方法,会导致缓冲区状态错乱、数据覆盖甚至 panic。这不是 Go 的 bug,是设计使然。
立即学习“go语言免费学习笔记(深入)”;
- 若需多 goroutine 写同一文件,要么加互斥锁(
sync.Mutex包裹Write+Flush),要么每个 goroutine 持有独立的BufferedFileWriter(但注意文件偏移竞争) - 更推荐方案:用 channel 聚合写请求,单个 goroutine 顺序消费并写入——既避免锁争用,又保证顺序和缓冲效率
- 切勿依赖
io.MultiWriter把多个bufio.Writer写向同一*os.File——它不解决并发写 buffer 的问题


















