Index Compression(压缩比vs解压效率)
主要是对倒排索引(inverted index)中的倒排列表(postings list)进行编码压缩。
编码方法:
1.D-gaps:对有序编号(如docid)进行差值(d-gaps)编码。(处理小数据需要小代码量,处理时间短)编码并没有定义存储数据的比特模式,所以他自身不节省任何空间。
2.Elias-γ Code结合了一元编码和二进制编码。编码数字k需要计算两个值:
3. Elias-δCode通过改变kd的编码方式,将kd分解为
Kdd使用一元编码,kdr用二进制编码, kr仍然用二进制编码
4. Variable Byte Code:每个字节的低7位是二进制数,高位是一个决定位。编码的最后一个字节高位位置为1,位置为0。处理器一般是以字节为处理单位,所以Variable ByteCode速度快,但是处理大数据压缩比不高。
5. Golomb编码中,整数x用两部分来表示,商和余数。商的计算公式为 ,余数的计算公式为r =( q*k )-1,在这里 k是 Golomb编码算法的基础,
如果 r< p,整 数可以用 ⌊log2k⌋位来存储 , 否则它将需要 ,在这里P是分界点,计算方法为p=2⌊log2k⌋+1-k。
当 r< p时,Golomb编 码用q个0,1个1,还有r的二进制表示。否则表示方法为 q个0,1个 1,以及 r + p的二进制表示。这样,整数 9可用 k = 3编码为00,1,11。
参数k的选择是至关重要的。如果选择得不好,编码后的整数会变得非常大 ,需要很长时间来解压 。Witten et al.(1994)认为假定倒排表 中的整数符合 Bernoulli模型, 则一列整数a的k值用 k≈0.69x平均值(a)来计算。
Williams 和Zobel描述了对Golomb编码实施优化的方法,并且认为常规的对整数的Golomb编码比Elias gamma编码和Elias delta编码解码更快并且更加节省空间。
6. BinaryInterpolative Coding二进制插入编码用相邻数的信息来编码一个单调递增的整数数列。
如果在整数数列X1中,对于任一给定的整数xi ,前一个数xi-1和后一个数xi-2是已知的,xi的大小是在(xi-1+1, xi-2-1) 的范围内,所需的最大位数为 log2(xi-1-xi-2-2) 。解码时需要xi-1和xi-2的信息,所以数列 X2是从原先的X1得到的,也就是说每个从表 X1 得到的整数都在X2中, 这样也就可以递归地进行编码 。
二进制插入编码(BIC)利用相邻两个数的信息,对单调递增的倒排列表进行紧凑的递归编码,不仅压缩率高,而且解码速度快;该算法还考虑了文档中词出现的频率分布,以聚类( clustering)的方式对倒排列表的压缩性能进行优化,有效地提高了索引的空间效率.
7.other compression:
InvertedIndex Compression Using Word-Aligned Binary Codes -Anh V, Moffat A.2005 (基于字行的二进制编码方式)
Performanceof Compressed Inverted List Caching In Search Engines -Zhang Jiang-Gong,LoneXiao Hui,Suel T.2008
Invertedindex compression and query processing with optimized document ordering -Yan H, Ding S, Suel T.2009
基于字行的二进制编码方式(Word-AlignedBinary Code,WABC)对索引进行压缩;该编码具有字节操作的优点,其紧凑的二进制特性不仅保证了索引的压缩性能,而且提高了查询时倒排列表的解码速度.
Performance ofCompressed Inverted List Caching In Search Engines中同时考虑了搜索引擎中的索引压缩与索引缓存机制,并对变比特编码等几种压缩算法的性能进行了比较分析.为了进一步提高检索性能.
Inverted indexcompression and query processing with optimized document ordering 对倒排列表文档标识(Identity,ID)整数集的升序特征进行了研究,通过更加紧凑的表示方法、快速求交集算法以及对文档ID顺序进行优化,提高查询效率。
"Index Compression Using 64-Bit Words", Anh, Moffat(最近才发现的好东东,对以往的压缩方法的概括集成。待了解。开源地址:http://ww2.cs.mu.oz.au/~alistair/coders-64bit/)
以及作者Alistair Moffat的相关研究:http://ww2.cs.mu.oz.au/~alistair/abstracts/
参考《Compression of inverted Indexes For FastQuery Evaluation》论文源码地址:http://www.seg.rmit.edu.au/projects.html)
对于posting 三元组<Docid, Frequence, Position>:
1. 首先对Docid,Position经过d-gaps编码处理。
2. 然后对posting中的3个元素采用不同的编码方法组合。如Dvbyte-FGol-PGamma
如果选择Elias-γ Code,Elias-δCode,VariableByte Code, Golomb进行实验对比,则共有24种组合方案。
这里只介绍了倒排索引中的对整型数据的编码压缩方法,相关的其他压缩方法参考wikipedia:http://en.wikipedia.org/wiki/Data_compression
2.Doclist压缩方法简介
附lemur中RVLCompress的压缩方法代码:(可变字节编码压缩方法v-bytes coding)
/******************************************************** @editor:weedge E-mail:weege@126.com @date:2011/08/30 @comment: 1.对int32进行压缩,返回压缩后的长度 2.对压缩byte型数据进行解压,返回解缩后的长度 *******************************************************/ #include "RVLCompress.hpp" #include <stdlib.h> #include <stdio.h> #define pow2_7 128 #define pow2_14 16384 #define pow2_21 2097152 #define pow2_28 268435456 #define pow2_31 2147483648U #define RVL_COMPRESS_MASK ((1<<7)-1)//0111 1111 #define RVL_COMPRESS_TERMINATE_BIT (1<<7)//左移7位,1000 0000 #define RVL_COMPRESS_BYTE( d, in, b ) d[b] = (char) ((in >> 7*b) & ((1<<7)-1))//取高7*N位 #define RVL_COMPRESS_TERMINATE( d, in, b ) d[b] = (char) ((in >> 7*b) | (1<<7))//取低7*N位,并置高位为1 ///return number of bytes in result static int compress_ints (int *data_ptr, unsigned char *out_ptr, int size); /// returns number of ints decompressed static int decompress_ints(unsigned char *data_ptr, int *out_ptr, int num_bytes); /*************************************** 对压缩byte型数据进行解压,返回解缩后的长度 @editor:weedge @parameter: data_ptr:待解压的byte型数据 out_ptr: 解压后的int32型数据 num_bytes:待解压的数据大小 **********************************/ int decompress_ints (unsigned char *data_ptr,int *out_ptr,int num_bytes) { unsigned char *data_end_ptr = data_ptr + num_bytes;//指向数组末尾 unsigned char *data_curr_ptr; int *out_ptr_end = out_ptr; for (data_curr_ptr=data_ptr; data_curr_ptr<data_end_ptr; out_ptr_end++) //遍历数组中的每个char { if (*data_curr_ptr & 128) //和1000 0000进行与操作,判断最高位是否为1 { *out_ptr_end = 127 & *data_curr_ptr; data_curr_ptr ++; } else if (*(data_curr_ptr+1) & 128) { *out_ptr_end = *data_curr_ptr | ((*(data_curr_ptr + 1) & 127) << 7); data_curr_ptr += 2; } else if (*(data_curr_ptr+2) & 128) { *out_ptr_end = *data_curr_ptr | (*(data_curr_ptr + 1) << 7) | ((*(data_curr_ptr + 2) & 127) << 14); data_curr_ptr += 3; } else if (*(data_curr_ptr+3) & 128) { *out_ptr_end = *data_curr_ptr | (*(data_curr_ptr + 1) << 7) | (*(data_curr_ptr + 2) << 14) | ((*(data_curr_ptr + 3) & 127) << 21); data_curr_ptr += 4; } else { *out_ptr_end = *data_curr_ptr | (*(data_curr_ptr + 1) << 7) | (*(data_curr_ptr + 2) << 14) | (*(data_curr_ptr + 3) << 21) | ((*(data_curr_ptr + 4) & 127) << 28); data_curr_ptr += 5; } } // for return (out_ptr_end - out_ptr); } /********************************************************** 注意:该int32数据在压缩前,进行d-gaps编码操作,节省压缩时间。 对int32进行压缩,返回压缩后的长度 @parameter: data_ptr:待压缩的int数据 out_ptr: 压缩后的byte数据 size:待压缩的数据大小 **********************************/ int compress_ints (int *data_ptr,unsigned char *out_ptr,int size) { int *data_end_ptr = data_ptr + size;//指向数组末尾 int *data_curr_ptr; unsigned int n; unsigned char *out_ptr_end = out_ptr; for (data_curr_ptr=data_ptr; data_curr_ptr<data_end_ptr; data_curr_ptr++) //遍历数组中的每个int { n = (unsigned int)*data_curr_ptr; if (n < pow2_7)//小于128 *out_ptr_end++ = 128 | n;//加128,即把n的二进制数值的首位置1 else if (n < pow2_14) //128<= n < 16384(2(14)) { *out_ptr_end = 127 & n;//和0111 1111进行与操作,取n的低7位 *(out_ptr_end + 1) = 128 | (n >> 7);//右移7位,取n的高7位,并置高位(8位)为1 out_ptr_end += 2; } else if (n < pow2_21) { *out_ptr_end = 127 & n;//和0111 1111进行与操作,取n的低7位 *(out_ptr_end + 1) = 127 & (n >> 7);//右移7位,取n的高7位,并置高位(8位)为1 *(out_ptr_end + 2) = 128 | (n >> 14); out_ptr_end += 3; } else if (n < pow2_28) { *out_ptr_end = 127 & n; *(out_ptr_end + 1) = 127 & (n >> 7); *(out_ptr_end + 2) = 127 & (n >> 14); *(out_ptr_end + 3) = 128 | (n >> 21); out_ptr_end += 4; } else { *out_ptr_end = 127 & n; *(out_ptr_end + 1) = 127 & (n >> 7); *(out_ptr_end + 2) = 127 & (n >> 14); *(out_ptr_end + 3) = 127 & (n >> 21); *(out_ptr_end + 4) = 128 | (n >> 28); out_ptr_end += 5; #if 0 if (n >= pow2_31) { cerr << "WARNING: value exceeded int limit in compression" << endl; } #endif } } // for return (out_ptr_end - out_ptr); } int main() { /*test compress_ints*/ /*test decompress_ints*/ return 0; }