倒排索引及其优化

197 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 26 天,点击查看活动详情

倒排索引

在搜索引擎搜索关键词时,如何快速地根据关键词,找到符合的文章?一个简单的思路是,对所有文章的所有单词,做一个单词到文章的映射,因为同一个单词可能在很多文章里,这里需要存储单词到文章列表的映射,这就是倒排索引。

image.png

例如我们查找的内容包含两个词,我们根据这两个单词,得到两个文章列表,结果就是这两个列表的交集。

  • 无序链表:暴力枚举O(nm)
  • 有序链表:双指针算法O(n+m)

两个 posting list 求交集是一个最重要、最耗时的操作。下面我们再进一步讨论可能的优化手段。

对求交集的优化

对于双指针算法,当一个list特别长,时间消耗就很大,我们可以利用调表快速跳过非结果值。

image.png

跳表法 image.png

二分法 我们也可以对有序存储在数组里的list进行二分查找。这样类似跳表,也可以logn快速找到下一个答案。

相互二分法

二分也可以进一步优化,也就是每一次对剩余比较长的部分二分。

image.png

哈希表法

list存入hash table,遍历短list看是否与长list有交集。O(m)

位图法加速倒排索引

相当于按位与操作,效率高。

局限性:仅适用于只存储 ID 的简单的 posting list,仅适用于 posting list 中元素稠密的场景,占用大量的空间。

联合查询的优化

有时候我们查询的词不只有两个,对于大于两个词的联合查询,情况更加复杂。 k个长度为n的list,顺序求交,复杂度O(kn)

调整次序法

类似哈夫曼编码的贪心思路,每次先合并最短的两个list。

image.png

快速多路归并法:利用跳表的性质,快速跳过多个元素,加快多路归并的效率。

  1. 将 4 个链表的当前第一个元素取出,让它们按照由小到大的顺序进行排序。然后,将链表也按照由小到大有序排列;
  2. 用一个变量 max 记录当前 4 个链表头中最大的一个元素的值;
  3. 从第一个链表开始,判断当前位置的值是否和 max 相等。如果等于 max,则说明此时所有链表的当前元素都相等,该元素为公共元素,那我们就将该元素取出,然后回到第一步;如果当前位置的值小于 max,则用跳表法快速调整到该链表中第一个大于等于max 的元素位置;如果新位置元素的值大于 max,则更新 max 的值。
  4. 对下一个链表重复第 3 步,就这样依次处理每个链表(处理完第四个链表后循环回到第一个链表,用循环数组实现),直到链表全部遍历完。

预先组合法 对于常见的联合查询,我们可以提前将结果算好,并将该联合查询定义一个 key。

缓存法

对用户的查询,用LRU保留一部分结果,当遇到热点查询时,可以有效地缓解压力。双向链表+hash:当一个元素被访问时,将它提到链表头。满时,删除链表尾元素即可。

image.png