应避免不设缓冲区的大文件流式处理,否则易因无节制缓存触发oom;bufio.scanner需显式调用scanner.buffer设置大行容量,手动read需预分配复用缓冲并正确截取buf[:n]。

大文件流式处理不设缓冲区,Go 程序大概率在读到几百 MB 时就因内存暴涨被系统 OOM kill——这不是配置问题,是 os.ReadFile、io.ReadAll、甚至 bufio.NewReader(file).ReadString('\n') 这类看似“流式”的调用底层仍会无节制缓存导致的必然结果。
bufio.Scanner 分行读文本必须调 scanner.Buffer
默认 bufio.Scanner 单行最大只允许 64KB,日志里带 base64 或长 traceID 就直接 panic:scanner: token too long。这不是 bug,是安全限制。
- 必须在
scanner.Scan()前显式设置:scanner.Buffer(make([]byte, 64*1024), 10*1024*1024)—— 第二个参数才是最大单行长度(例如 10MB) -
scanner.Text()返回的是拷贝,安全;若需字节级操作(如跳过 BOM),改用scanner.Bytes()避免 string 转换开销 - 最后一行没换行符?
scanner.Scan()在 EOF 时仍返回该行,无需额外判空——前提是没触发上面那个缓冲溢出
手动 file.Read 控制二进制块节奏
遇到 protobuf 流、加密块、固定帧结构等非文本格式,Scanner 失效,必须自己控制 Read。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 预分配一个复用缓冲区:
buf := make([]byte, 4*1024*1024)(4MB),别在循环里反复make - 用
n, err := file.Read(buf),不是io.ReadFull:后者在最后一块不足长度时直接报io.ErrUnexpectedEOF,而这是正常结束信号 - 判断 EOF 要分两种:
if n > 0 && err == io.EOF→ 最后一块有效;if n == 0 && err == io.EOF→ 空文件或刚读完 - 写入目标时务必传
buf[:n],传整个buf会把上一轮残留脏数据一起写进去
io.CopyBuffer 写慢设备必须换大缓冲
NFS、Ceph、USB 盘或云盘上,默认 32KB 缓冲会让 io.Copy 吞吐暴跌,甚至卡死报 broken pipe——本质是小 write 导致 syscall 过多 + 等待叠加。
- 实测将缓冲设为
make([]byte, 1024*1024)(1MB),NFS 上提速 3–5 倍 - 不要用
bytes.Buffer接io.Copy处理大文件:它会在内存攒完整份,700MB 文件直接 OOM - 写 HTTP 响应体或上传 multipart 时,应直连目标
io.Writer,跳过中间内存缓冲
缓冲区大小不是越大越好:10MB 缓冲对 SSD 可能浪费,对 NFS 却刚刚好;关键不在“设多大”,而在“谁来管理”——让调用方传入 dst []byte,或用 sync.Pool 复用,比包内部维护一个不断膨胀的私有缓冲更可控。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










