因为bufio.NewWriter是带缓冲的,数据只写入内存缓冲区,不调Flush()就不会落盘;常见现象是文件为空或内容不全,必须显式调用Flush()并检查错误,defer仅在函数正常返回时执行。

bufio.NewWriter 为什么写不进文件?
因为 bufio.NewWriter 创建的是带缓冲的写入器,数据先写入内存缓冲区,不会立即落盘。没调用 Flush() 就关闭文件或程序退出,缓冲区里的内容就丢了。
常见错误现象:os.OpenFile 打开文件、bufio.NewWriter 包装、写几行、直接 file.Close() —— 文件为空或只有部分数据。
- 必须在关闭前显式调用
wr.Flush() - 如果用
defer wr.Flush(),注意它在函数返回时才执行,若函数提前 panic 或 return,可能来不及刷出 -
WriteString、Write、WriteLine等方法都只操作缓冲区,不触发磁盘写入
bufio.Writer 的缓冲区大小怎么设才合适?
默认缓冲区是 4096 字节(bufio.DefaultWriterSize),对大多数日志或配置写入够用;但批量写小数据(比如每行几十字节)时,过小会导致频繁刷盘,过大则延迟高、内存占用多。
使用场景决定取舍:
立即学习“go语言免费学习笔记(深入)”;
- 写日志:4KB~64KB 均可,兼顾响应与吞吐;
bufio.NewWriterSize(file, 32*1024) - 导出 CSV/JSON 流:建议 256KB 起,减少系统调用次数
- 内存受限环境(嵌入式):可压到 512 字节,但要接受更多
Flush()开销 - 缓冲区设为 0 会退化为无缓冲写,等价于直接用
file.Write,失去WriteString等便利性
WriteString + Flush 和 Fprintln 哪个更快?
性能差异微乎其微,关键看可读性和是否需要格式化。两者底层都走同一缓冲路径,Fprintln 实际内部也调用 wr.WriteString 拼接换行符。
但要注意:
-
Fprintln(wr, "hello")自动加\n;wr.WriteString("hello\n")需手动处理换行 -
Fprintf支持格式化,但有反射开销,高频写固定结构体时,预拼字符串再WriteString更稳 - 所有方法在缓冲满或显式
Flush()时才触发一次系统调用,不是每次调用都 syscall
文件写入中途崩溃,如何避免缓冲区数据丢失?
bufio 本身不提供原子写或崩溃恢复机制。要降低风险,得靠组合策略:
- 写关键数据前,先
wr.Flush()强制落盘(适合低频、高可靠性场景) - 用
file.Sync()在Flush()后同步元数据和数据(慢,但能保证磁盘物理写入) - 更稳妥的做法是「写临时文件 →
os.Rename替换原文件」,利用文件系统原子重命名特性 - 不要依赖
defer wr.Close()——Close()内部会Flush(),但若Flush()失败(如磁盘满),Close()也会返回 error,必须检查
缓冲区是性能优化手段,不是持久化保障。真正怕丢数据的地方,得靠 flush + sync + 临时文件三件套,少一个环节都可能在断电或 kill -9 时丢数据。


















