mmap 不适合全文搜索,仅加速按偏移随机读;真正适用场景是已知偏移、固定结构或索引驱动的快速字节访问,如固定行长日志行提取、二进制索引文件随机读等。

别直接上 mmap 做全文搜索——它不加速“找关键词”,只加速“按偏移随机读”。真要搜大文件,mmap 得配合其他策略用,否则容易白忙活还崩出 SIGBUS。
为什么 mmap.ReadAt 比 grep 快,但不能替代 grep
mmap 把文件变成内存切片,bytes.Index 或 bytes.Contains 就能直接扫,省去系统调用和内核缓冲区拷贝。但这只在你**已经知道大概位置**时才快:比如查数据库 WAL 文件里 offset=12840 的记录,或日志中第 100 万行开头的字段。
- 顺序全扫 50GB 文件?
mmap会触发密集缺页中断,bufio.Scanner+strings.Contains更稳 - 想支持正则、模糊匹配、上下文高亮?
mmap返回的是[]byte,你得自己实现regexp的底层匹配逻辑,没意义 - 文件含 1 亿行,你却对每行都
bytes.Index扫一遍——这和ReadLine没本质区别,只是换了个内存拷贝路径
真正该用 mmap 的搜索场景:已知偏移 / 固定结构 / 索引驱动
适合把 mmap 当“零拷贝字节视图”用,而不是当“全文搜索引擎”用。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- 日志文件每行固定长度(如 512 字节),要查第 99999 行:直接
mm[99999*512 : 99999*512+512]下标取,毫秒级 - 二进制索引文件(如 LevelDB 的 .ldb),key 按 offset 存储,用
mm.ReadAt(buf, keyOffset)随机读,比os.File.ReadAt快 2–3 倍 - 预构建布隆过滤器后,用
mmap流式解析日志提取order_id字段再Add,避免ReadLine多一次内存分配
用 mmap-go 写安全搜索代码的硬性条件
用 github.com/edsrzf/mmap-go 是底线,裸调 syscall.Mmap 在 Windows 上直接失败,在 Linux 上极易 panic: invalid argument。
- 映射前必须
f.Stat()拿真实大小,别信配置里的 “1GB” ——mmap.Open("x.dat")自动按文件大小映射,最省心 - 只读搜索用默认
mmap.Open();若需边读边写(如打标记),必须mmap.OpenFile(f, mmap.RDWR),且后续改完立刻mm.Flush() - 用
unsafe.Slice算偏移时,确保offset + length ,Go 1.21+ 推荐写法:<code>unsafe.Slice(unsafe.Add(unsafe.Pointer(&mm[0]), offset), length) - Windows 下文件大小不是 64KB 整数倍?
CreateFileMapping报The parameter is incorrect—— 先os.Truncate(f, alignedSize)
搜索结果错位、SIGBUS、数据不落盘?先看这三处
这些不是偶发 bug,是 mmap 语义决定的必然行为,漏掉就翻车。
- 搜索到某行,但下标越界访问:因为日志行尾没换行符,
bytes.IndexByte(mm, '\n')返回 -1,后续mm[pos:]越界 → 必须检查返回值 - 多进程同时搜同一文件并写标记,A 进程改了 B 看不到:没用
MAP_SHARED(mmap-go默认是,但你自己调syscall.Mmap时得显式传) - 程序退出后文件没更新:写了
mm[100] = 1却忘了mm.Flush()→msync不触发,数据卡在 page cache
最易被忽略的点:mmap 生命周期与 GC 无关,defer mm.Close() 只释放映射,不刷盘也不等写完成;Flush() 必须在你确认写完后手动调,不能靠 finalizer 或 defer。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










