bufio.Scanner 不适合读大文件,因其默认64KB缓冲区遇超长行会panic;应改用bufio.Reader配合自定义缓冲、分块读取、Seek优化及并发限流等策略。

用 bufio.Scanner 读大文件会崩溃?别用它
bufio.Scanner 默认缓冲区只有 64KB,遇到超长行(比如单行几百 MB 的日志或 JSON)直接 panic:scanner: token too long。它本质是为“行清晰、长度可控”的场景设计的,不是为大文件流式处理准备的。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 明确放弃
bufio.Scanner处理未知格式或可能含超长行的大文件 - 改用
bufio.Reader+ReadBytes('\n')或ReadString('\n'),自己控制缓冲区大小和错误恢复 - 若必须按行处理且行长不可控,先用
reader.Peek(n)预判长度,再决定是否分配内存
内存暴涨?用 io.ReadFull + 固定 buffer 分块读取
一次性 os.ReadFile 或 bytes.Buffer 加载 GB 级文件,Go 进程 RSS 瞬间飙高,还可能触发 GC 频繁停顿。关键不是“读得慢”,而是“不该把整块文件塞进堆里”。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
os.Open获取*os.File,再套一层bufio.NewReaderSize(f, 1024*1024)(如 1MB 缓冲) - 循环调用
reader.Read(buf),buf是复用的[]byte(例如make([]byte, 64*1024)) - 避免在循环内做字符串转换(
string(b))——这会逃逸并复制内存;如需解析,直接操作[]byte
需要随机跳转读取?file.Seek() 比反复打开更高效
有些场景(如解析带偏移索引的日志、分片校验)需要从文件任意位置开始读。频繁 os.Open + os.Stat + 定位,开销远高于单次打开后多次 Seek。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 打开一次文件后,用
file.Seek(offset, io.SeekStart)移动读取位置,再调用file.Read(buf) -
Seek是系统调用,但比重新 open/close 快一个数量级;注意它不改变bufio.Reader内部状态,所以 Seek 后应直接对底层*os.File读,或重建bufio.Reader - Windows 上对大于 2GB 文件使用
int64偏移量,确保用io.SeekCurrent等常量而非字面量
并发读多个大文件?小心 fd 耗尽和磁盘寻道
起 100 个 goroutine 各自 os.Open 不同大文件,看似并发,实际可能卡在系统 fd 限制(Linux 默认 1024)、磁盘 IOPS 瓶颈,甚至因频繁 seek 导致吞吐反降。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
semaphore(如golang.org/x/sync/semaphore)限制同时打开的文件数,例如设为 8–16 - 对同一物理磁盘上的多个大文件,并发读不如顺序读 —— SSD 尚可,HDD 上 seek 延迟会吃掉所有并发收益
- 如果必须多文件处理,优先考虑 mmap(
syscall.Mmap),让 OS 统一管理页缓存,但注意munmap和内存映射冲突问题










