来看看倒排索引压缩。压缩是拿CPU换IO的最重要手段之一,不论索引是放在硬盘还是内存中。索引压缩的算法有几十种,跟文本压缩不同,索引压缩算法不仅仅需要考虑压缩率,更要考虑压缩和解压性能,否则会解压太慢而起不到CPU换IO的作用。早期的索引设计里,在尝试了几十种编码之后,基本都确定性采用差分编码+可变长字节编码。差分的目的在于让索引的文档ID尽可能小,因为压缩小的整数总是比大整数更有效。在索引构建算法中,有一类工作叫做“文档重排”,目的就是通过对文档索引顺序的重新排列,使得索引posting list中的文档ID之差最小,这样就可以让压缩算法更有效的工作,从而使得索引总体积最小。当然这样的工作在实际中价值有限,因为索引的构建速度以及增量构建同样非常重要,耗费大量时间在文档重排上,对于静态数据集合才更加有效。可变长字节编码大概是最早的索引压缩编码,思路简单到无以复加的地步——每个字节的第一位为flag,表示是否继续使用下一个byte,剩下7位为有效位,所有的有效位组成数字的2进制表示。但是它却非常有效,因为解压速度非常快。采用差分和可变长组合手段,假定文档ID采用32位整数,那么索引体积基本上可以压缩到之前的1/2到1/4之间。这种压缩手段占据了主流,几乎所有的开源搜索(Lucene,Sphinx),商业搜索都采用这种方式进行,Google则引入了Group可变长字节编码,以4个整数为一组进行压缩,这样压缩率更高。我们可以找到阿里实现的Group可变长字节编码的实现,因此很可能淘宝商品搜索也采用了这种方式。

大约2007年开始,一种名为PForDelta的索引压缩算法开始引起更多人的重视,这是一种压缩率更高并且解压速度更快的算法。有研究表明,索引压缩的过程中相邻文档ID差值为1的情况大约占10%,而PForDelta算法对小差值的情况,特别有优势。假定一个索引块为8个值(已经做过差分),80%的情况下值小于32,小于32的值均可以用一个b = 5bit的数来表示。建立这样一个结构:8*b-bit的常规部分,看作是一个位数组,每个元素占b-bit定长空间,余下的为异常部分,看作是一个整形数组,每个元素占4字节定长空间。假定有这样一个序列:23, 41, 8, 12, 30, 68, 18, 45,通过PForDelta方法的构造得到如下压缩结构:

椭圆框所示的部分为常规部分,常规部分的第一个值1,表示从该地址开始,跳过1个地址,就可以找到下一个异常值的位置,同理第三个值3表示,跳过3个地址,就是下一个异常值的位置。常规值从前到后存储,异常值从后向前存储。PForDelta压缩是基于块来进行,目前常用的选择是128。把处理异常值的方式做改进,采用可变长字节或者其他算法(目前最先进的是S9或者S16)压缩,就是改进型的NewPFor和OptPFor压缩算法。

PForDelta及其系列改进从07年发明以来已经逐渐成熟,后边的工程实践中引入了SSE指令加速,使得解压速度可以更快。一些主流商业搜索引擎已经广泛采用,也包含上面提到的淘宝商品搜索。然而,技术革新的步伐并没有停止。PForDelta这一族算法,压缩是按照区块来进行的,这意味着如果希望仅仅访问其中某一个元素,那么需要把整个区块进行解压。有时候我们并不希望总是全部解压,从而可以做到对压缩数字的随机读取。在2012年的时候,出现了Quasi-succinct索引。它可以提供元素的随机访问而不需要全部解压。注意这里又出现了succinct字样,是因为该索引对于压缩接近信息熵的下界,这符合succinct的定义。Quasi-succinct索引的性能跟最好的区块压缩算法压缩解压性能基本一致,采用的是Elias-Fano编码,但是压缩率缺却并不高,因此会导致索引体积膨胀——尽管如此,索引所占的体积仍然少于常规的可变长字节编码。Elias-Fano编码针对随机元素的解压非常快速,但是如果需要解压全部元素,它的速度还是不能最先进的批量解压算法例如NewPFor和OptPFor快。

Elias-Fano编码过程如下:把一组整数的最低l位连接在一起,同时把高位以严格单调增的排序划分为桶。用0表示桶的存在,用1表示桶里的元素,有多少元素就有多少个1。

图中的序列为2,3,5,7,11,13,24,如果期望定位大于6的位置,那么根据6/2^2就可以定位到大于6的桶,然后在桶内线性扫描即可。可以看到,低l位的存在,就是起到了桶定位的用途,从而避免全部解压,这可以类比于常规索引中的跳跃表,跳跃间隔为2^l。

Quasi-succinct索引在MG4J的开源搜索引擎中得到了应用,MG4J是个人认为的Java版本的开源搜索引擎中最具备研究和学习价值的,不仅仅在于高于Lucene的代码质量,更在于对于数据结构与算法孜孜不倦的创新。当然,由于不善宣传,出自学校而并没有吸引更多的开发人员加入社区,
知晓并愿意改进MG4J的人寥寥无几,这跟Lucene形成了鲜明的对比。因此,即便在技术领域,先进性也往往让步于宣传。

Partitioned(分区块) Elias-Fano编码,这篇文章获得了2014年SIGIR会议最佳论文,它是针对Elias-Fano编码进行的改进。仍然由Quasi-succinct的作者提出,主要解决Quasi-succinct索引的压缩率问题——回归区块压缩手段,把数字序列划分区块,每个区块内单独用Elias-Fano编码,同时,为了确保仍然具备随机访问的特性,把区块的边界数字再次单独拿Elias-Fano编码压缩,因此形成了一个二级结构。根据作者的试验,分区Elias-Fano编码比最快的PForDelta编码OptPFor速度和压缩率上均有超越,但压缩率大大超过后者(2倍以上)。因此,在随机访问,压缩率,解压性能上达到了很强的综合性能,荣膺最佳论文实至名归。

创新依然在继续,自从SSE加速指令引入到PForDelta的实现之后,针对SIMD指令如何设计良好的压缩算法也成为工程和学术的研究重点。亚马逊旗下搜索引擎A9.com就曾经提出了针对SIMD加速的可变长字节编码实现,而在2013年底,加拿大LICEF研究中心的Lemire提出了基于SIMD bitpacking的压缩编码SIMD-BP128,其解压速度是迄今为止最快的,超过OptPFor的2倍(一秒钟可以解压10亿整数),当然在压缩率上并没有达到高指标。

压缩可以说是索引设计中的第一考虑要素,盘点上面的列表,NewPFor,OptPFor,Quasi-succinct(Elias-Fano),Partitioned Elias-Fano,SIMD-BP128,都是业界最先进的选择,设计时需要根据自己的要求做出取舍。

转自:http://chuansong.me/n/2035211

倒排列表压缩算法汇总——分区Elias-Fano编码貌似是最牛叉的啊!的更多相关文章

  1. wukong引擎源码分析之索引——part 1 倒排列表本质是有序数组存储

    searcher.IndexDocument(0, types.DocumentIndexData{Content: "此次百度收购将成中国互联网最大并购"}) engine.go ...

  2. 图解Skip List——本质是空间换时间的数据结构,在lucene的倒排列表,bigtable,hbase,cassandra的memtable,redis中sorted set中均用到

    Skip List的提出已有二十多年[Pugh, W. (1990)],却依旧应用广泛(Redis.LevelDB等).作为平衡树(AVL.红黑树.伸展树.树堆)的替代方案,虽然它性能不如平衡树稳定, ...

  3. 倒排列表求交集算法 包括baeza yates的交集算法

    #ifndef __INTERSECT_HPP__ #define __INTERSECT_HPP__ #include "probe.hpp" namespace themas ...

  4. ES里设置索引中倒排列表仅仅存文档ID——采用docs存储后可以降低pos文件和cfs文件大小

    index_options The index_options parameter controls what information is added to the inverted index, ...

  5. Poseidon 系统是一个日志搜索平台——认证看链接ppt,本质是索引的倒排列表和原始日志数据都存在HDFS,而文档和倒排的元数据都在NOSQL里,同时针对单个filed都使用了独立索引,使用MR来索引和搜索

    Poseidon 系统是一个日志搜索平台,可以在百万亿条.100PB 大小的日志数据中快速分析和检索.360 公司是一个安全公司,在追踪 APT(高级持续威胁)事件,经常需要在海量的历史日志数据中检索 ...

  6. Lucene 4.X 倒排索引原理与实现: (2) 倒排表的格式设计

    1. 定长编码 最容易想到的方式就是常用的普通二进制编码,每个数值占用的长度相同,都占用最大的数值所占用的位数,如图所示. 这里有一个文档ID列表,254,507,756,1007,如果按照二进制定长 ...

  7. 一些开源搜索引擎实现——倒排使用原始文件,列存储Hbase,KV store如levelDB、mongoDB、redis,以及SQL的,如sqlite或者xxSQL

    本文说明:除开ES,Solr,sphinx系列的其他开源搜索引擎汇总于此.   A search engine based on Node.js and LevelDB A persistent, n ...

  8. AC日记——单词倒排 1.7 28

    28:单词倒排 总时间限制:  1000ms 内存限制:  65536kB 描述 编写程序,读入一行英文(只包含字母和空格,单词间以单个空格分隔),将所有单词的顺序倒排并输出,依然以单个空格分隔. 输 ...

  9. OpenJudge计算概论-单词倒排【把句子内单词的顺序翻转】

    /*================================================== 单词倒排 总时间限制: 1000ms 内存限制: 65536kB 描述 编写程序,读入一段英文 ...

随机推荐

  1. android学习之路资料集合

    版权声明:本文为 stormzhang 原创文章,可以随意转载,但必须在明确位置注明出处!!! 这篇博客背后的故事 一路走来很不容易,刚好知乎上被人邀请回答如何自学android编程, 就借这个机会在 ...

  2. IP访问频率限制不能用数组循环插入多个限制条件原因分析及解决方案

    14.IP频率限制不能用数组循环插入多个限制条件原因分析及解决方案: define("RATE_LIMITING_ARR", array('3' => 3, '6' => ...

  3. [Windows Server 2008] 查看ASP详细错误信息方法

    ★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com ★ 护卫神·V课堂是护卫神旗下专业提供服务器教学视频的网站,每周更新视频. ★ 本节我们将带领大家:查看IIS下 ...

  4. 設置VS2015

    減少VsHub的資源占用 VsHub在某些環境下會挂,原因見這個帖子 其作用簡述如下: First, the service that detects and auto-updates extensi ...

  5. haproxy故障处理

    1. haproxy 在配置健康检查的时候,默认没有配置页面检查 ,通过端口状态来检测.后端IIS web服务开始可能 是一个站点,或者采用了基于域名的配置方式,导致目前站点停了,后端主机不能被hap ...

  6. 使用GetMirror一次镜像多个实体

    public static void GetMirror(this ObjectIdCollection ids, Point3d p1, Point3d p2, bool s, params Ent ...

  7. Beauty of Array ZOJ - 3872(思维题)

    Edward has an array A with N integers. He defines the beauty of an array as the summation of all dis ...

  8. 举枪消灭"烂代码"的实战案例

    前言 之前我写过一篇如何少写PHP "烂"代码 https://segmentfault.com/a/11...感觉很多新人对此不太理解.今天以打卡功能为例,去讲解其中的奥秘.那篇 ...

  9. php 导出Excel 不用安装插件、开启配置

    function export_csv($filename, $data) { header("Content-type:text/csv"); header("Cont ...

  10. Maven学习总结(5)——聚合与继承

    Maven学习总结(五)--聚合与继承 一.聚合 如果我们想一次构建多个项目模块,那我们就需要对多个项目模块进行聚合 1.1.聚合配置代码 <modules> <module> ...