高性能索引模型的核心是让os.Seek+io.ReadAt少跳、少读、不重复:跳表存offset/length、倒排索引配行偏移数组、位图手写[]uint64、地理索引用2dsphere+GeoJSON;跳表value禁存整行,倒排需预建lineOffset数组,位图避用[]bool。

直接说结论:高性能索引模型不是选“最炫”的结构,而是让 os.Seek + io.ReadAt 尽可能少跳、少读、不重复——跳表存 offset/length、倒排索引配行偏移数组、位图用 []uint64 手写、地理索引必须用 2dsphere + GeoJSON,这四类组合覆盖 90% 的本地高性能索引场景。
跳表只存 offset 和 length,别存整行内容
跳表在 Go 中没有标准库实现,但关键不在实现多复杂,而在 value 存什么。存整行字符串会把内存吃光,尤其处理日志等大文件时。
- 错误做法:
map[string]string存完整日志行 → 2GB 文件轻松占满 5GB 内存,GC 频繁触发 - 正确做法:
map[string][]struct{ offset int64; length int },key 是可排序字段(如时间戳字符串或哈希前缀) - 构建时先调
os.Stat().Size()预估总行数,避免切片反复扩容;插入前用sort.Search定位插入点,维持有序 - 查询后调
file.Seek(offset, io.SeekStart),再用io.ReadFull(file, buf[:length])精确读取,不依赖bufio.Scanner的复用底层数组
倒排索引必须预建行偏移数组,不能裸用 map[string][]int
map[string][]int 是轻量倒排索引的起点,但仅靠它查不到磁盘位置——它只给出“第几行”,没告诉你“这一行从哪开始”。
- 按行切分时,必须额外维护一个
lineOffset []int64数组,其中lineOffset[i]是第 i 行在文件中的字节偏移 - 插入时,每读一行就记录当前
file.Seek(0, io.SeekCurrent)值到lineOffset,不要用scanner.Bytes()后再算长度,易错 - 查词后拿到行号列表,再查
lineOffset[lineNo]得物理偏移,最后Seek+ReadAt定位 - 中文分词必须用
github.com/go-ego/gse并设seg.WithFrequency(false),否则每个词多带频次字段,内存多占 30%+
位图必须用 []uint64 手写,避开 []bool 的三重坑
[]bool 在千万级索引场景下性能劣化 3–5 倍,不是写法问题,是底层设计缺陷。
立即学习“go语言免费学习笔记(深入)”;
- 越界风险:
Get(i)返回false时,无法区分是“未设置”还是“索引越界”,必须手动加if uint(len(b.bits)) - 位运算陷阱:不能直接写
1 ,i ≥ 64 时结果为 0;必须拆成 <code>wordIdx := i / 64和bitIdx := i % 64,再用(b.bits[wordIdx] & (1 - 伪共享:并发写不同 bit 若落在同一
uint64元素上,CPU 缓存行会反复无效化;解决方案是分段(sharding),例如每桶 64K bit,桶索引由i / bitsPerShard算出,每桶配独立sync.Mutex - 索引变量统一用
uint64,禁用int—— 特别是当位图可能超2^31时
地理索引必须用 2dsphere + GeoJSON,$near 查询要带 $geometry
地理查询慢,90% 是因为数据格式或索引类型不匹配,和 Go 代码关系不大。
- location 字段必须是标准 GeoJSON 对象:
bson.M{"type": "Point", "coordinates": []float64{116.4789, 39.9223}};[lng, lat]顺序不能反,且缺type字段就无法创建2dsphere索引 - 索引必须建
"location": "2dsphere",不能用2d;建错会导致$nearSphere报错或返回平面距离 - 查询必须用
$near+$geometry,且单位是米:"$near": bson.M{"$geometry": geo, "$maxDistance": 5000};漏掉spherical: true或用$near而非$nearSphere会退化为平面计算 - Go 中可用结构体标签强制约束:
type Location struct { Type string `bson:"type"`; Coordinates []float64 `bson:"coordinates"` }
真正卡住性能的,往往不是算法多精妙,而是 os.Seek 跳错了位置、lineOffset 没对齐、uint64 位运算越界、或者 coordinates 里经纬度写反了——这些细节不验证,再好的索引结构也白搭。



















