应使用流式读取处理gb级大文件,因os.readfile会将整个文件加载进内存导致oom;推荐bufio.reader分块读取,配合csv.reader单行处理和自定义缓冲区大小以平衡性能与内存。

别用 os.ReadFile 读大文件,它会直接 OOM;真正能扛住 GB 级文件的,只有流式读取——逐块拿、边读边处理、不囤积内存。
为什么 os.ReadFile 一读大文件就崩
它内部调用 bytes.Buffer.Grow 预分配空间,但文件大小未知时容易反复 realloc,碎片多、延迟高;更关键的是,它把整个文件塞进一个 []byte,1GB 文件 ≈ 1GB 堆内存,GC 瞬间卡死,系统可能直接 kill 进程。错误现象通常是:fatal error: runtime: out of memory 或进程无声退出。
适用场景仅限配置文件、JSON 小模板等几 KB~几 MB 的内容。超过 10MB 就该换方案。
用 bufio.Reader 分块读取最稳
这是通用性最强、可控性最高、上线最放心的方式。核心是手动控制每次读多少字节,不依赖行、不假设格式,适配文本、二进制、自定义协议都行。
-
bufio.NewReader(file)包一层,再调r.Read(buffer),返回实际读到的字节数n -
io.EOF是正常结束信号,不是错误,必须单独判断,否则会误报错 - 缓冲区大小建议设为
256 * 1024(256KB)到1024 * 1024(1MB):太小(如 4KB)导致系统调用频繁;太大(如 10MB)浪费堆、拖慢 GC - 记得用
defer file.Close(),bufio.Reader不自动关底层文件
读 CSV 大文件别调 csv.NewReader(f).ReadAll()
这个组合看似方便,实则危险:ReadAll() 会把全部记录加载成 [][]string,每行字段越多、字符串越长,内存开销指数级上升。10 万行 × 平均 20 字段 × 每字段 100B ≈ 200MB 内存,还没算切片头和 GC 压力。
正确做法是用 csv.Reader.Read() 单行流式处理:
- 每次只读一行到
[]string,处理完立刻丢弃,内存驻留恒定 - 配合
bufio.NewReader套一层,还能进一步减少系统调用次数 - 若某列含超长文本(如 Base64 图片),需提前调
csv.NewReader(f).FieldsPerRecord = -1允许变长字段,否则会 panic
遇到超长行(>64KB)时 bufio.Scanner 会挂
默认 bufio.MaxScanTokenSize = 64 * 1024,碰到单行日志、嵌套 JSON 或 CSV 中的富文本字段,直接报错:scanner: token too long。这不是 bug,是设计限制。
临时解法可以调大 buffer:
sc := bufio.NewScanner(f) sc.Buffer(make([]byte, 0, 64*1024), 10*1024*1024) // 上限 10MB sc.Split(bufio.ScanLines)
但注意:第二个参数是硬上限,超了仍 panic;如果根本不确定最长行多长,不如直接换回 bufio.Reader + r.ReadString('\n'),自己处理截断和拼接逻辑——虽然多几行代码,但边界清晰、无隐式失败。
真正难的不是选哪个 API,而是想清楚:你是否真的需要“一次全读”?多数时候,流式处理不仅省内存,还让错误定位更早、进度可控、中断安全。别让 ReadAll 和 ReadFile 的便利性,掩盖了它在大文件面前的脆弱本质。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











