直接用bufio.writer能显著提升写文件性能,但必须显式调用flush()才能真正落盘,否则数据仅存于内存缓冲区;默认4kb缓冲区需按场景调整,日志宜设64kb、批量导出可设256kb–1mb;并发写应通过channel串行化,避免panic或锁瓶颈;flush()后关键数据还需file.sync()确保物理落盘。

直接用 bufio.Writer 就能显著提升写文件性能,但不调 Flush()、缓冲区设错、混用底层写入,都会让优化失效甚至丢数据。
为什么默认 file.Write 性能差
每次调用 file.Write 都触发一次系统调用(syscall),小数据高频写时,内核态/用户态切换开销远超实际 I/O 时间。比如写 10 万行日志(每行约 100B),纯 file.Write 可能触发 10 万次 syscall;而用 bufio.Writer 默认 4KB 缓冲,最多只需 ~2500 次,实测耗时可从秒级降到毫秒级。
常见错误现象:
- 程序跑完,文件存在但内容为空或只有一半
- 日志服务重启后丢失最后几条记录
- 压测时 CPU 花在
sys_write上占比奇高(go tool pprof可验证)
bufio.Writer 必须显式 Flush() 才算真正写入
w.Write() 只是把数据拷进 Go 的内存缓冲区,不落盘;Flush() 才真正调用 write() 系统调用。忽略它,等于没写。
实操要点:
- 别只依赖
defer w.Flush():函数中途return或 panic 会跳过它,最后一块数据就丢了 - 错误必须在
Flush()后检查:file.Write几乎总返回nil错误,真出问题(磁盘满、权限不足)只在Flush()时暴露 - 长周期服务(如日志 daemon)不能只靠结尾一次
Flush(),得配合定时(time.Ticker)或批量(如每 500 条)策略
缓冲区大小不是默认就好,得按场景调
默认 4KB 缓冲区适合低频小写入,但对日志、导出等场景太小——刷盘太勤,性能上不去;设太大又浪费内存、延迟升高。
推荐设置:
- 日志追加写(每行 ~100B):
64 * 1024(64KB) - 批量导出 JSON/CSV(单次写 >1KB):
256 * 1024~1024 * 1024(256KB–1MB) - 内存受限或嵌入式环境:
8 * 1024(8KB),避免 GC 压力突增
设置方式:w := bufio.NewWriterSize(file, 64*1024)
并发写同一个文件时,别锁 bufio.Writer,改用 channel 串行化
bufio.Writer 本身不是 goroutine 安全的,多个 goroutine 直接共用会 panic;加 sync.Mutex 保护写入又容易成为瓶颈。
正确做法:
- 生产者 goroutine 把日志/数据发到带缓冲的 channel(如
make(chan string, 1000)) - 单一消费者 goroutine 持有
bufio.Writer,持续rangechannel 并批量写入 - 消费者退出前必须显式
w.Flush(),不能defer—— 因为range不会自动结束,需监听donechannel 主动退出
最易被忽略的一点:Flush() 只保证数据进内核页缓存,不等于落物理磁盘;关键数据(如“任务已完成”标记)必须在 Flush() 成功后立刻跟 file.Sync(),否则断电或崩溃仍可能丢数据。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











