搜索引擎倒排索引表压缩:gamma编码

当你每天打开电脑,在百度搜索框中输入你要搜索的内容,按下回车之后,你可能不会意识到,有无数台主机在飞速运转,对比了数百万条记录,经过初步结果集生成、相关度打分、结果排序、摘要生成之后,才最终在你的屏幕上打出了你想要的结果。这一切仅仅发生在几毫秒之间。

是什么保证了如此迅速的检索速度呢?良好的索引构建是其中的要素之一。通常情况下,搜索引擎内部会为每个网页或文章分配一个数字id,用这个id代表这个网页或者文章。构建索引的时候,采用分词工具将这些网页或者文章分成一个个词,并网页id存储在称为倒排索引表的数据结构中。

由于网络空间巨大,对应的倒排索引表所占的空间也很大,对倒排索引表进行压缩显得非常必要。由于倒排索引表中存储的全部都是数字,对其进行压缩有着专门的方法,Gamma编码就是其中的一种。Gamma编码是一种基于位的变长编码,介绍它之前先说一下一元编码。

一元编码:将 n 表示成 n 个1和最后一个0,

比如: 3的一元码是 1110

40的一元码是 11111111111111111111111111111111111111110

  1. Gamma将数G字表示成长度(length)和偏移(offset)两部分;
  2. offset部分对应G的二进制编码,只不过将首部的1去掉。

    例如 13 → 1101 → 101 = 偏移;

  3. length部分采用一元编码,表示偏移部分的位数。

    例如G=13(偏移101),偏移长度为3,一元编码1110

  4. G的?编码就是将长度部分和偏移部分两者联接起来得到的结果。

以下代码实现了对倒排索引表进行Gamma编码:

@java
import java.util.Arrays;
import java.util.Iterator;

public class GammaBit {
    public static void main(String[] args) {
        int[] testNumbers = {1, 2, 3, 4, 9, 13, 24, 511, 1025};
        GammaBit gammaBitBuf = new GammaBit();
        for(int i = 0; i < testNumbers.length; i++){
            int n = testNumbers[i];
            GammaBit gammaBit = GammaBit.getGammaBit(n);
            gammaBitBuf.append(gammaBit);
        }
        System.out.println("Gamma Code:" + gammaBitBuf);
        Iterator<Integer> iterator = gammaBitBuf.iterator();
        System.out.print("original numbers are:");
        while(iterator.hasNext())
            System.out.print(" " + iterator.next());
        System.out.println();
    }
    private byte[] bytes;
    private int bitLength; // length of bits used
    private byte[] bitMap = {
        0x1, 0x2, 0x4, 0x8,
        0x10, 0x20, 0x40, (byte)0x80
    };
    public GammaBit(byte[] bytes, int bitLength){
        int newSize = 1;
        while(newSize <= bytes.length)
            newSize <<= 1;
        this.bytes = Arrays.copyOf(bytes, newSize);
        this.bitLength = bitLength;
    }
    public GammaBit(){
        bytes = new byte[8];
        Arrays.fill(bytes, (byte)0);
        bitLength = 0;
    }

    public int getBitLength(){
        return bitLength;
    }
    public int getInnerBytesLength(){
        return bytes.length;
    }
    public void append(GammaBit gammaBit){
        int lengthAll = gammaBit.bitLength + this.bitLength;
        if(lengthAll < 0)
            throw new RuntimeException("bitLength is too large!");
        int bytesNeeded = (lengthAll + 7) >>> 3;
        if(bytesNeeded >= Math.pow(2, 31))
            throw new RuntimeException("bitLength is too large!");
        if(bytesNeeded > this.bytes.length){
            int newSize = 1;
            while(newSize <= bytesNeeded)
                newSize <<= 1;
//          System.out.println("resize from " + this.bytes.length + " to " + newSize);
            this.bytes = Arrays.copyOf(this.bytes, newSize);
        }
        for(int i = 0; i < gammaBit.bitLength; i++){
            boolean value = gammaBit.getABit(i);
            this.setABit(i + this.bitLength, value);
        }
        this.bitLength += gammaBit.bitLength;
    }
    public static GammaBit getGammaBit(int n){
        if(n <= 0)
            throw new RuntimeException("number is not bigger than zero: " + n);
        GammaBit gammaBit = new GammaBit();
        int lenOffset = 0;
        while((n >= (1 << ++lenOffset)));
        lenOffset -= 1;
        int bitBegin = gammaBit.bitLength;
        int bitEnd = bitBegin + lenOffset;
        for(int i = bitBegin; i < bitEnd; i++){
            gammaBit.setABit(i, true);
        }
        gammaBit.setABit(bitEnd, false);
        gammaBit.bitLength += lenOffset + 1;
        String binaryStr = Integer.toBinaryString(n);
        for(int i = 1; i < lenOffset + 1; i++){
            boolean value = (binaryStr.charAt(i) == ‘1‘);
            gammaBit.setABit(i + gammaBit.bitLength - 1, value);
        }
        gammaBit.bitLength += lenOffset;
        return gammaBit;
    }
    private void setABit(int p, boolean isValueOne){
        if(isValueOne)
            this.bytes[p / 8] |= bitMap[p % 8];
    }
    private boolean getABit(int p){
        return (this.bytes[p / 8] & bitMap[p % 8]) != 0;
    }
    public String toString(){
        StringBuilder builder = new StringBuilder();
        for(int i = 0; i < this.bitLength; i++){
            if(getABit(i))
                builder.append(‘1‘);
            else
                builder.append(‘0‘);
        }
        return builder.toString();
    }
    public Iterator<Integer> iterator(){
        return new GammaBitIterator();
    }
    /**
     * @return the bytes used by bits
     */
    public byte[] getBytes(){
        int bytesUsed = (bitLength + 7) >>> 3;
        return Arrays.copyOf(bytes, bytesUsed);
    }
    private class GammaBitIterator implements Iterator<Integer>{
        private int nowBitPosition = 0;
        public boolean hasNext(){
            return nowBitPosition < GammaBit.this.bitLength;
        }
        public Integer next(){
            int i = 0;
            for(; i + nowBitPosition < GammaBit.this.bitLength
                    && getABit(i + nowBitPosition); i++);
            nowBitPosition += i + 1;
            int length = i;
            int offsetValue = 1;
            for(i = 0; i < length; i++){
                offsetValue <<= 1;
                if(getABit(nowBitPosition + i))
                    offsetValue += 1;
            }
            nowBitPosition += length;
            return offsetValue;
        }
    }
}
时间: 2024-10-25 23:03:26

搜索引擎倒排索引表压缩:gamma编码的相关文章

优化系列 | InnoDB引擎数据表压缩特性测试

一.前言Innodb Plugin引擎开始引入多种格式的行存储机制,目前支持:Antelope.Barracuda两种.其中Barracuda兼容Antelope格式.另外,Innodb plugin还支持行数据压缩特性,不过前提是采用Barracuda行存储格式.表空间启用压缩的前提是innodb表空间文件存储格式修改成:Barracuda,需要修改2个选项:innodb_file_format = "Barracuda"innodb_file_format_max = "

oracle 11G表压缩

最近一套生产库表空间一直告警在90%以上,但的磁盘硬件资源又不足,整个库已经达到26T.库里存储了近4年的数据,与业务沟通说历史数据基本上不会做操作,但是又不能归档,所以想到了压缩表来节省表空间. 随着数据库的增长,我们可以考虑使用oracle的表压缩技术.表压缩可以节省磁盘空间.减少data buffer cache的内存使用量.并可以显著的提升读取和查询的速度.当使用压缩时,在数据导入和DML操作时,将导致更多的CPU开销,然而,由于启用压缩而减少的I/O需求将抵消CPU的开销而产生的成本.

【SQL Server性能优化】SQL Server 2008该表压缩

当数据库是比较大的,而当你想备份,我们可以启动数据库备份压缩.这项由于备份文件比较小的压缩,所以整个备份的更快的速度,同时还低了磁盘空间的消耗. 当然还有一方面.肯定会添加cpu的消耗.只是一般的server都是多核.所以实际上对系统不会有大的影响. 事实上.不仅能够在备份的时候压缩,在SQL Server 2008中.我们还能够对表和索引进行压缩,以下通过压缩前.压缩后的比較.来展示SQL Server 2008强大的表及索引的压缩功能. 这里在公司測试数据库找了一个中型的表,共同拥有943万

SQL Server 表压缩

表压缩有三个选项 1.page 2.row 3.none ----------------------------------------------------------------------------------------------------------------------- 三种设置方式: 第一种:定义时指定 create table table_name(....) with (data_compression = none | page | row); 第二种:修改时指

mysql修改数据库编码(数据库字符集)和表的字符编码的方法

Mysql数据库是一个开源的数据库,应用非常广泛.以下是修改mysql数据库的字符编码的操作过程和将表的字符编码转换成utf-8的方法,需要的朋友可以参考下. mysql将表的字符编码转换成utf-8: alter table tb_anniversary convert to character set utf8; 修改数据库mysql字符编码为UTF8: 步骤1:查看当前的字符编码方法: mysql> show variables like'character%'; +-----------

Oracle 表压缩(Table Compression)技术介绍

Oracle 表压缩(Table Compression)介绍 1.官方文档说法: As your database grows in size, consider using table compression. Compression saves disk space, reduces memory use in the database buffer cache, and can significantly speed query execution during reads. Compr

【SQL Server性能优化】SQL Server 2008之表压缩

当数据库比较大,而要进行备份时,我们可以启动数据库备份的压缩,这样减少对于磁盘空间的消耗. 其实,不仅可以在备份的时候压缩,在SQL Server 2008中,我们还可以对表进行压缩,下面通过压缩前.压缩后的比较,来展示SQL Server 2008强大的表压缩功能. 这里在测试数据库找了一个较大的表,共有9439661 条记录, 1.压缩前表的大小 SP_SPACEUSED 'TB_WCB' /* name rows reserved data index_size unused TB_WCB

Oracle压缩功能小结2—预估表压缩的效果

在使用压缩之前,我们可以估算一下使用压缩能够拥有多大的效果. 11gr2以前可以使用dbms_comp_advisor,具体代码已经在附件中给出.只需要执行两个文件dbmscomp.sql和prvtcomp.plb,然后使用DBMS_COMP_ADVISOR.getratio存储过程即可.不再详细描述. SQL> set serveroutput on SQL> execdbms_comp_advisor.getratio('SH','SALES',10) Sampling table: SH

MySQL数据库、表的字符编码

用MySQL命令行新建数据库和表时默认的字符编码是latin1,但是在实际开发过程中一般都是使用utf8格式的编码.操作如下: 1.修改数据库字符编码 mysql> alter database mydb character set utf8 ;2.创建数据库时,指定数据库的字符编码 mysql> create database mydb character set utf8 ;3.查看mysql数据库的字符编码 mysql> show variables like 'character