背景
搜索引擎中,倒排索引是用于实现高效检索的一个核心数据结构。大数据集的倒排索引同样很大,因此产生了倒排索引压缩技术,降低读取索引时的磁盘I/O时间,以及在内存、CPU缓存之间进行数据传输的时间。
倒排索引压缩方面的研究已有接近50年的历史,目前仍然在持续更新,每年都有新算法提出。随着计算机硬件的发展,现在的搜索系统倾向于让索引数据常驻内存,因此索引压缩技术的关注点也在变化,从早期的专注于优化压缩率提升磁盘I/O效率,变为更重视解码效率和查询性能。
在58当前的搜索场景下,索引热数据通常都常驻内存,有可能出现服务器上内存使用比例大幅超过CPU使用比例,因此索引压缩的主要效果在于降低内存占用,平衡资源利用率,避免内存成为资源瓶颈。
本文将简单介绍在面向内存的场景下适用的一些压缩算法,并结合58搜索的场景,重点介绍其中一种算法的原理、实现和优化方法。
倒排索引压缩算法概览
给定一个文档集,其倒排索引表示的是文档集中的每个词到其所出现的文档的集合的映射。为了便于处理,索引中会为每个文档分配一个从0递增的id,因此倒排索引一般包含一个词典,其中每个词对应一个升序排列的文档id数组,我们称其为倒排链(posting list)。这些倒排链一般会占倒排索引的大部分空间,因此倒排索引压缩技术所要解决的问题,可以抽象为如何对有序整数序列进行压缩。
除了优化压缩率以外,压缩技术还要保证压缩后的索引数据能够被高效检索。检索的形式通常为多个词的与、或、非等逻辑关系查询,具体到倒排索引这一层,则表现为多个倒排链之间的交集、并集、差集运算。在58搜索场景下,最常见的是交集运算。在交集运算中,倒排链需要支持的基本操作,是给定一个文档id,查找它在这个倒排链中的位置。
基于上一节所述的场景,我们调研了业界多种压缩算法,下面将介绍其中一些典型算法,例如常见的PForDelta[1]及其衍生的算法如NewPFD[2],OptPFD[3]等,然后是较新的一些算法,如获得SIGIR会议2014年最佳论文的PEF(Partitioned Elias-Fano)[4],以及2017年提出的MILC[5]。
PForDelta
PForDelta算法是2005年提出的算法,压缩方法主要包括分块、差分编码和异常值处理。
它的基本思想是把倒排链分为多个块(chunk),例如每128个id分为一个块,然后对块进行压缩。
为了压缩块内每个元素的空间,可以利用id升序排列的特性,只存相邻id的差值(即delta)。例如原始chunk为{10023, 10102, 10123, 10192, 10197, 10212},则每个id需要 14 bit。如果只存储差值,那么除了第一个id以外,其余元素可存储为{79, 21, 69, 5, 15},每个元素只需要 7 bit。