os.ReadFile在小文件上快、大文件变慢,因其内部一次性分配整文件大小的切片,小文件内存分配快,大文件易触发GC及内存拷贝开销。

为什么 os.ReadFile 在小文件上快,大文件却变慢?
os.ReadFile 内部会一次性分配足够容纳整个文件的切片,对小文件(
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 小文件(os.ReadFile,代码简洁且无明显性能损失
- 大文件或不确定大小:改用
os.Open+bufio.Reader或io.Copy,避免内存峰值 - 若需随机访问(如跳过头部、读某一段),优先用
os.OpenFile配合file.Seek和file.Read
缓冲区大小设成多少才不浪费也不卡顿?
bufio.NewReaderSize 的缓冲区不是越大越好。Linux 默认页大小是 4KB,内核 I/O 调度也倾向以 4KB–64KB 为单位处理;设成 1MB 可能导致单次系统调用读太多,阻塞时间变长,还浪费内存。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 通用场景:用
bufio.NewReader(默认 4KB),平衡吞吐与延迟 - 顺序读大文件(如备份、ETL):设为 64KB–256KB,例如
bufio.NewReaderSize(file, 1 - 高并发小读(如 HTTP body 解析):保持默认或略减至 2KB,降低 goroutine 内存占用
- 避免设为非 2 的幂(如 50KB),部分底层实现会对齐,实际仍按 64KB 分配
io.Copy 比手动循环 Read/Write 快在哪?
io.Copy 不只是语法糖。它内部做了三件事:自动选择最优缓冲区(io.DefaultBufSize = 32KB)、跳过中间内存拷贝(对支持 ReaderFrom/WriterTo 的类型,如 *os.File,直接调用 splice 系统调用)、并复用临时缓冲区减少 GC。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 文件到文件、文件到网络连接等“管道式”传输,无条件优先用
io.Copy(dst, src) - 不要自己写
for { n, _ := src.Read(buf); dst.Write(buf[:n]) }—— 这种写法漏掉错误检查、没处理 partial write、且无法利用splice - 若需在拷贝中修改数据(如加解密、压缩),再考虑
io.Copy配合io.Pipe或自定义io.Reader
写文件时 os.O_SYNC 和 file.Sync() 到底该谁负责?
加 os.O_SYNC 会让每次 Write 都同步落盘,极大拖慢吞吐(尤其机械盘);而只靠最后调一次 file.Sync(),又可能在崩溃时丢失最后一批未刷盘的数据。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 日志类追加写:用
os.O_APPEND | os.O_WRONLY打开,每条日志后调file.Sync()(或批量几条后 sync),不用O_SYNC - 关键配置/数据库 WAL:必须用
os.O_SYNC,接受性能代价保一致性 - 普通输出文件(如导出 CSV):写完关闭前调一次
file.Sync()即可,os.File.Close()本身不保证落盘 - 注意:
file.Sync()是阻塞调用,别在热路径里高频调用
Read 或多了一次 Sync 上。











