安全读写字节需确保不越界且不append,即下标访问必须在mm.len()范围内,重切不可超限;映射前须用file.stat().size()校验文件长度≥映射长度,避免sigbus。

映射后怎么安全读写字节,而不是 panic SIGBUS
直接用 mm.Bytes() 返回的切片下标访问是安全的,但必须遵守两个硬约束:不能 append,不能超出 mm.Len() 范围重切。常见错误是写成 data[1000:2000] 却没校验 2000 ,运行时不报错,一访问就 <code>SIGBUS。
推荐做法:
- 始终用
mm.Len()判断边界,别信len(data)(它可能被截断或未更新) - 需要偏移访问时,Go 1.21+ 用
unsafe.Slice(unsafe.Add(unsafe.Pointer(&data[0]), offset), length),前提是offset + length - 避免用
reflect.SliceHeader手动构造切片头——容易漏设cap,导致越界写入
写完数据为什么文件没更新,什么时候必须调 mm.Flush()
mmap-go 默认用 MAP_SHARED,修改会立刻反映到页缓存,但不会自动落盘。程序崩溃、断电或系统重启后,未刷入的数据就丢了。这不是 bug,是 mmap 的设计特性。
关键规则:
-
defer mm.Unmap()或defer mm.Close()不触发刷盘,只解映射 -
mm.Flush()等价于msync(MS_SYNC),必须显式调用,尤其在关键写入后、服务退出前 - 用
mmap.RDONLY打开的映射,Flush()直接返回错误,不用调 - 高频小写入别每次写都
Flush(),攒批后统一刷,否则 I/O 开销反超os.File.WriteAt()
为什么映射大文件失败,报 cannot allocate memory
不是内存不足,而是撞了内核限制 /proc/sys/vm/max_map_count。比如映射多个 >100GB 文件,或单个文件分段多次映射,容易触发该阈值,默认通常为 65530。
临时解决:
- Linux 下执行
sysctl -w vm.max_map_count=262144 - 确认文件长度 ≥ 映射长度:
fi, _ := file.Stat()后传fi.Size()给mmap.Map(),别硬写常量 - Windows 上完全不支持
syscall.Mmap,必须用mmap-go,否则直接返回ENOSYS
什么场景该用 mmap,什么场景该绕开
mmap 不是“大文件就上”,性能收益取决于访问模式。随机跳转读写固定偏移(如日志按 offset 查行、B+ 树索引定位)才真正受益;顺序扫全量反而更慢。
选型参考:
- 适合:
io.ReaderAt/io.WriterAt频繁调用、多进程共享同一文件、要求微秒级延迟的随机访问 - 不适合:顺序遍历 50GB 日志、小文件(
- 危险点:映射后文件被
truncate或删除,后续访问直接SIGBUS;磁盘满时写操作也会崩
SIGBUS。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











