📘 教案 25:倒排索引(Inverted Index · 工程级)
一、问题模型
设有文档集合:
D=d1,d2,...,dn\]\[ D = {d_1, d_2, \\dots, d_n} \]\[D=d1,d2,...,dn
每个文档由一系列词组成。
给定查询词 ( q ),需要快速找到:
di∣q∈di\]\[ { d_i \\mid q \\in d_i } \]\[di∣q∈di
二、朴素方法及其问题
方法:逐文档扫描
对每个文档判断是否包含词 (q)
复杂度
O(∣D∣⋅L)\]\[ O(\|D\| \\cdot L) \]\[O(∣D∣⋅L)
其中 (L) 为文档平均长度。
问题
- 无法扩展到大规模数据
- 查询延迟高
三、倒排索引的定义
倒排索引是从"词 → 文档列表"的映射结构
正排索引(Forward Index)
text
doc1 → [word1, word2, ...]
倒排索引(Inverted Index)
text
word1 → [doc1, doc3, doc7]
word2 → [doc2, doc3]
👉 查询时直接定位相关文档集合
四、核心结构
1. 词典(Dictionary)
存储:
term→posting list\]\[ term \\rightarrow posting\\ list \]\[term→posting list
2. 倒排列表(Posting List)
对于每个词:
text
word → [docID1, docID2, ...]
扩展信息(实际系统)
text
word → [(docID, tf, positions...)]
五、构建过程(关键)
Step 1:分词(Tokenization)
将文档拆分为词项
Step 2:归一化
- 小写化
- 去停用词
- 词干提取
Step 3:构建映射
text
term → docID
Step 4:排序
每个 posting list 按 docID 排序
六、查询处理
1. 单词查询
直接返回 posting list
2. 多词查询(AND)
例如:
text
q = "apple AND banana"
执行:
posting list 的交集
算法(双指针)
text
A: [1,3,5,8]
B: [3,5,7]
过程:
- 比较 A[i] 和 B[j]
- 相等 → 加入结果
- 小的指针前进
复杂度
O(∣A∣+∣B∣)\]\[ O(\|A\| + \|B\|) \]\[O(∣A∣+∣B∣)
七、优化:跳跃表(Skip Pointers)
在 posting list 中增加跳跃指针:
text
1 → 5 → 9 → ...
👉 快速跳过不可能匹配的区间
八、排名(Ranking)
查询不仅要"找到",还要"排序"。
TF-IDF
定义:
TF=词频\]\[ TF = \\text{词频} \]\[TF=词频
IDF=logNdf\]\[ IDF = \\log \\frac{N}{df} \]\[IDF=logdfN
得分:
score(d,q)=TF⋅IDF\]\[ score(d, q) = TF \\cdot IDF \]\[score(d,q)=TF⋅IDF
含义
- 出现频率高 → 更重要
- 出现在少数文档 → 更有区分度
九、工程结构(关键)
1️⃣ 倒排索引存储
- 磁盘文件(通常压缩)
- 按 term 排序
2️⃣ 构建过程
👉 使用外部排序:
- term → docID
- 排序
- 合并
3️⃣ 更新问题
不能直接修改:
👉 采用 LSM-Tree 思想:
- 新数据 → 新索引段
- 后台合并
十、压缩(非常重要)
posting list 通常很大:
方法
- 差分编码(Delta Encoding)
- 变长编码(Varint)
👉 减少存储与 I/O
十一、系统整体结构
text
用户查询
↓
解析(分词)
↓
查倒排索引
↓
合并 posting list
↓
计算得分(TF-IDF)
↓
返回 Top-K
十二、与前面内容的关系
| 模块 | 对应算法 |
|---|---|
| 构建索引 | 外部排序 |
| 存储 | LSM-Tree |
| 查询 | 双指针 / 二分 |
| 加速 | Bloom Filter |
👉 这是完整链路
十三、本质总结(严肃表达)
倒排索引通过建立词项到文档集合的映射,将原本需要扫描全文的查询问题转化为集合运算问题,从而在大规模文本检索中实现高效查询。