bufio.reader缓冲区大小需按场景设定:日志/csv按最长行+128字节(如8kb设64kb),大文件顺序读用64–128kb,网络响应用4–32kb;scanner需预设buffer防超长行,写入后必须flush,scan()后须检查err()。

bufio.Reader 读大文件时缓冲区设多大才合适
默认 4KB 缓冲区在小文本上够用,但对 >100MB 日志或 CSV 文件,系统调用次数会明显偏高——实测 strace -e trace=read 可见 read() 调用频次下降 5–8 倍,吞吐翻倍。
- 日志/CSV 行处理:按单行预期最大长度 + 128 字节,比如最长 8KB 就设
64 * 1024 - 纯顺序读大文件(如归档解析):用
64 * 1024或128 * 1024,兼顾内存占用与 syscall 合并效果 - 网络响应或 HTTP header 解析:
4 * 1024–32 * 1024更稳妥,避免延迟抖动 - 别设
math.MaxInt32或盲目上1024 * 1024——不可控输入下容易 OOM,GC 压力也会上升
Scanner 报 scanner: token too long 怎么办
这不是文件太大,是某一行超长(比如嵌了 base64 的日志)撑爆了默认 64KB 缓冲区。错误发生在 scanner.Scan() 内部,不查 scanner.Err() 就捕获不到,程序可能静默跳过后续所有行。
- 必须提前调
scanner.Buffer(make([]byte, 64*1024), max),例如支持 1MB 行:scanner.Buffer(make([]byte, 64*1024), 1024*1024) -
scanner.Text()返回的字符串底层复用缓冲区,下次Scan()就覆盖;需长期保存时,得显式拷贝:string(scanner.Bytes())或append([]byte{}, scanner.Bytes()) - 如果输入不可控(如用户上传),别依赖
Scanner;换reader.ReadBytes('\n')或自己用Read()+ 固定切片循环扫描换行符更安全
bufio.Writer 写完文件内容却为空?检查 Flush
bufio.NewWriter 的数据只在三种情况下真正落盘:缓冲区满、显式调 w.Flush()、或 w.Close()。但 Close() 是“尽力而为”,失败时不报错也不重试。
- 写完必须调
w.Flush(),不能只靠defer w.Close()——若函数 panic 或提前 return,Flush()可能根本没执行 - 更稳妥的收尾写法:
defer func() { w.Flush(); f.Close() }() - 日志类高频写场景慎用超大缓冲(如
1024 * 1024):crash 时最多丢 1MB 数据,且延迟高;建议32 * 1024–256 * 1024平衡 io 和可靠性 -
io.Copy场景别套bufio.Writer:它内部已用 32KB 缓冲零拷贝优化,再包一层反而冗余
Scanner.Err() 才是真实错误源,Scan() 返回 true 不代表成功
scanner.Scan() 返回 true 只表示成功读到一个 token,不表示没错误。这是最常被忽略的点,导致静默失败——比如磁盘满、权限不足、连接中断,Scan() 都可能返回 true,但实际数据已损坏或截断。
- 每次循环后必须检查
scanner.Err(),尤其在Scan()返回false时——Err()才告诉你到底是io.EOF还是真出错了 - 混用
bufio.Reader和bufio.Scanner会导致读位置错乱:它们各自维护独立缓冲区,底层file的 offset 不同步 - 需要 peek 下一个字节、discard 一段数据、或精确控制边界(如 HTTP header/body 分界),必须用
bufio.Reader,Scanner封装太深,连错误类型都藏起来了
缓冲区不是越大越好,也不是加了就一定快;关键在匹配场景——行长度、IO 模式、错误容忍度,三者缺一不可。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











