直接用os.File.Write写小数据很慢,因为每次调用都触发高开销系统调用write(2),而bufio.Writer通过4KB缓冲区合并小写入、减少系统调用次数;缓冲区大小需权衡内存与延迟,日志可用8KB,关键数据宜设64–256KB并显式Flush,否则数据滞留内存导致丢失。

为什么直接用 os.File.Write 写小数据很慢
因为每次调用 os.File.Write 都会触发一次系统调用(write(2)),而系统调用开销远高于内存操作。尤其在循环中反复写入几字节,性能会断崖式下降——不是代码写错了,是没绕过内核的“门禁”。
常见现象:日志逐行写、CSV 逐行生成、JSON 流式序列化时 CPU 不高但耗时很长,strace 一看全是 write 调用。
- 每写 1 字节都进一次内核?真会这样
- 即使文件在 SSD 上,系统调用本身已是瓶颈
-
bufio.Writer的默认缓冲区是 4KB,意味着最多攒够 4KB 才真正落盘
bufio.NewWriter 的缓冲区大小怎么设才合适
默认 4KB 对多数场景够用,但不是越大越好。缓冲区本质是内存+延迟的权衡:大缓冲能减少系统调用次数,但会增加内存占用和数据滞留时间(比如程序崩溃时未 flush 的内容就丢了)。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 日志类场景(可容忍少量丢失):用默认值或
bufio.NewWriterSize(f, 8*1024) - 关键数据流(如数据库导出、配置生成):设为 64KB–256KB,但必须在关闭前显式
w.Flush() - 极小写入(如传感器每秒写 16 字节):反而可以设小点(
512),避免长时间不触发 flush - 别用
math.MaxInt或超大值——缓冲区分配失败会 panic,且无意义
忘记 Flush 导致数据“消失”的真实原因
bufio.Writer 是包装器,它把数据先写进自己的内存缓冲,只有缓冲满、调用 Flush 或 Close 时才真正交给底层 os.File。所以如果只写不 flush,程序退出时缓冲区被 GC 回收,数据就永远卡在内存里了。
典型错误模式:
- 用
defer w.Close()但中间 panic 了 →Close没执行 → 数据丢失 - 写完直接
return,忘了Flush - 误以为
w.Write返回nil错误就代表写成功了(其实只是写进缓冲区)
安全写法示例:
w := bufio.NewWriter(f)
defer w.Flush() // 不要 defer w.Close()
// ... 写操作
if err := w.Flush(); err != nil {
return err // 显式检查 flush 错误
}
什么时候不该用 bufio.Writer
缓冲不是银弹。以下情况加了 bufio.Writer 反而更慢或更危险:
- 单次写入远大于缓冲区(比如一次写 10MB):缓冲层纯属多余,还多一次内存拷贝
- 需要实时可见(如调试日志要立刻看到):缓冲会延迟输出,应改用
os.Stderr或带\n后手动Flush - 底层 Writer 本身已缓冲(如
gzip.Writer、json.Encoder):再套一层bufio.Writer可能导致双缓冲,浪费内存且不提升性能 - 写入目标是管道或网络连接(
net.Conn)且对延迟敏感:缓冲可能引入不可控延迟
真正影响效率的,从来不是“用了没用”,而是“该不该用”和“用完有没有善后”。


















