索引分片
将过大的倒排索引按词条范围拆分成多个小文件,使客户端搜索时只需下载命中的分片,大幅降低带宽占用。
#type / concept
#status / growing
#tech / dev / frontend
#resource / pagefind
[!info] related notes
索引分片
一句话定义
索引分片(Index Sharding)是将一个过大的倒排索引按词条范围拆分成多个小文件的策略,使客户端搜索时只需下载命中的那个分片,而不是整个索引。
核心机制 / 工作原理
为什么需要分片
如果一个网站有几千篇文章,倒排索引中可能包含几十万甚至上百万个词条。把所有词条放在一个索引文件中,这个文件本身也可能达到数 MB 大小。
用户每次搜索时,如果都要下载这个完整的索引文件,首屏体验会很差——尤其是在移动网络环境下。
分片的思路
核心思路是把大索引表按某个键(通常是词条的首字母、哈希值或词条范围)拆分成多个小片段:
shard_a-d.json → 包含 a 到 d 开头的词条
shard_e-h.json → 包含 e 到 h 开头的词条
shard_i-l.json → 包含 i 到 l 开头的词条
...
shard_w-z.json → 包含 w 到 z 开头的词条
同时维护一个很小的元数据表(meta index),记录每个分片包含哪些词条范围。这个元数据表通常只有几 KB。
查询流程
- 浏览器首先只加载很小的元数据表(几 KB)
- 用户输入查询词(比如 “proxmox”)
- 查元数据表,判断这个词条属于哪个分片
- 只下载那一个分片文件(通常也只有几 KB)
- 在分片内执行搜索,返回匹配的文档列表
这样无论索引总量多大,单次搜索的网络开销都只和一个分片的大小成正比。
Pagefind 中的实现
Pagefind 是索引分片策略的典型实践。它在 pnpm build 构建完成后运行,用 Rust 构建倒排索引,然后按词条范围将索引切分成几百个极小的分片文件(每个通常只有几 KB),存放在 pagefind/ 目录下。
搜索时,浏览器先加载一个很小的 pagefind 入口文件和元数据表,然后根据查询词动态拉取对应的分片。这就是为什么一个包含 2400 多篇文章、28 万个词条的知识库,搜索首屏加载可以控制在 30KB 以内。
与全量索引的对比
| 环节 | 全量索引(单文件) | 索引分片 |
|---|---|---|
| 首屏加载 | 下载整个索引文件(可能数 MB) | 只下载元数据表(几 KB) |
| 搜索时 | 在已下载的完整索引中查找 | 定位分片 → 下载分片 → 在分片中查找 |
| 结果渲染 | 数据已全在内存,直接取用 | 按需调用 result.data() 懒加载摘要 |
| 适合规模 | 小站(< 100 页) | 中大型站(数百到数万页) |
分片在其他领域的类比
分片策略不限于搜索索引,在分布式系统中也很常见:
- 数据库分片(Database Sharding):按哈希或范围把数据分到不同数据库节点,减轻单节点压力
- CDN 分片:把静态资源分散到不同域名或节点,用户就近获取
- 消息队列分片(如 Kafka Partition):把消息流分散到多个分区,支持并行消费
核心思想都一样:把大规模数据拆成小块,让每次操作只需接触其中一小部分。