os.ReadFile读大文件会卡住是因为它一次性加载全部内容到内存,易触发OOM;应改用bufio.Scanner配合适当缓冲区,或io.ReadAt实现并发安全读取。
os.ReadFile读大文件会卡住因为os.ReadFile会一次性把整个文件加载进内存,文件大小超过几百MB时,不仅GC压力陡增,还可能触发OOM。这不是“慢”,是设计上就不该用它读大文件。
os.Open + io.ReadAll,没有流式控制[]byte,无法复用缓冲区,每次调用都分配新内存bufio.Scanner逐行读但遇到长行就panicbufio.Scanner默认最大行长度是64KB,超长行(比如单行JSON、base64编码块)直接报scanner: token too long。不能只改Split函数,得重设缓冲区。
sc := bufio.NewScanner(f)
buf := make([]byte, 10*1024*1024) // 10MB缓冲
sc.Buffer(buf, 10*1024*1024)
sc.Split(bufio.ScanLines)
for sc.Scan() {
line := sc.Bytes() // 注意:line是buf子切片,别逃逸出循环
}
sc.Buffer()和sc.Split(),顺序不能反sc.Text()会拷贝字符串,高吞吐下建议用sc.Bytes()避免重复分配io.ReadAt比Seek+Read更稳在多goroutine并发读同一文件时,file.Seek()操作不是线程安全的——它修改文件内部偏移量,多个goroutine互相覆盖。而io.ReadAt传入明确偏移,不依赖文件状态。
file.ReadAt(p []byte, off int64)替代file.S
eek()+file.Read()
off必须在文件范围内,否则返回io.EOF或io.ErrUnexpectedEOF
ReadAt可安全并发;但写入时仍需同步mmap适合只读大文件但Windows支持弱Go标准库没内置mmap,得靠golang.org/x/sys/unix(Linux/macOS)或golang.org/x/sys/windows(Windows)。Windows的CreateFileMapping行为和POSIX差异大,尤其对稀疏文件或网络驱动器容易失败。
unix.Mmap + unix.Munmap,性能接近零拷贝SEC_COMMIT标志和页面对齐,错误码更难调试真正卡点往往不在读取本身,而在后续解析——比如把每行JSON反序列化成map[string]interface{},这比IO慢十倍。先确认瓶颈在IO还是CPU,别一上来就换mmap。