flush()是必做动作,因bufio.writer默认仅缓存写入数据,不调用flush()则数据几乎肯定未落盘;其错误检查不可省略,且缓冲区大小需依场景合理设置。

不调用 Flush(),写入的数据大概率不会落盘——这不是“可能丢”,而是“几乎肯定没写”。
为什么 Flush() 不是可选项,而是必做动作
bufio.Writer 的设计就是“攒着写”:所有 WriteString()、Write() 调用都只往内存缓冲区里填字节,不碰底层文件或连接。系统调用只有三种时机触发:缓冲区满、显式调用 Flush()、或者底层资源被关闭(但关闭时 bufio.Writer 自己不负责 flush)。
常见错误现象:
- 程序运行完,文件为空或只有前几行
- 日志写入后立即用
os.Stat()查大小,发现没变 - 网络写入(如
net.Conn)后对端收不到数据,抓包也看不到 payload
根本原因:你忘了把缓存里的数据“推下去”。
Flush() 必须检查错误,且不能只看 Write 的返回值
Write() 方法在绝大多数情况下返回 nil 错误——它只管把数据塞进缓冲区,哪怕底层连接已断,它也照收不误。真正和磁盘/网络打交道的是 Flush()。
实操建议:
- 每次写完关键数据(比如一条完整日志、一个协议帧)后,立刻
if err := w.Flush(); err != nil { /* 处理 */ } - 不要依赖
defer w.Flush()做兜底:函数 panic 或提前 return 时,defer可能不执行;更稳妥的是在关键路径末尾主动 flush 并检查 - 对网络写入,
Flush()失败通常意味着连接已断,后续再写会 panic,必须及时退出或重连
缓冲区大小影响 Flush() 触发频率和延迟
默认缓冲区是 4KB(bufio.NewWriter(f)),这意味着:平均要写够 4096 字节才会自动 flush 一次。如果你每条日志只有 100 字节,那得写 40 条才刷一次——延迟高达数秒甚至更久(取决于写入节奏)。
调整建议:
- 实时日志场景:用
bufio.NewWriterSize(f, 4096)或更小(如 1024),缩短延迟 - 批量导出大文件:用
bufio.NewWriterSize(f, 64*1024),减少 flush 次数,提升吞吐 - 别设超过 256KB:收益趋平,GC 压力上升,且单次 flush 阻塞时间变长
- 注意磁盘块大小:设为 4KB、8KB 等整数倍,对某些文件系统更友好
刷新失败后,缓冲区状态不可靠,别尝试“重试 flush”
Flush() 报错(比如 write: broken pipe 或 no space left on device)后,bufio.Writer 内部的 err 字段会被置为该错误,之后所有写操作(Write() 等)都会直接返回该错误,不再写入缓冲区。
此时不能简单地“再调一次 Flush()”来补救——缓冲区可能已部分写入、部分卡住,状态不一致。正确做法是:
- 记录错误并放弃当前 writer
- 关闭底层资源(如
f.Close()) - 必要时重建 writer(比如换文件、重连)
容易被忽略的一点:bufio.Writer 本身没有恢复机制,它的设计就是“一次出错,整体失效”。任何想绕过错误继续写的尝试,最终都会导致数据错乱或 panic。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











