lucene源代码学习之LZ4压缩算法在lucene中应用

LZ4算法又称为Realtime Compression Algorithm,在操作系统(linux/freeBSD)、文件系统(OpenZFS)、大数据(Hadoop)、搜索引擎(Lucene/solr)、数据库(Hbase)……都可以看到它的身影,可以说是一个非常通用的算法。LZ4最突出的地方在于它的压缩/解压速度。

基础知识

理解Lucene中LZ4算法的实现,需要有以下两点基础知识:

1、 理解Lucene里面的packedInts.

关于PacedInts,可以参考http://sbp810050504.blog.51cto.com/2799422/1531624

2、 理解素数乘法Hash

在LZ4中,用到了魔数2654435761L,这个数是2到2^32间黄金分割的素数,

2654435761
/ 4294967296 = 0.618033987

2654435761L用 signed int表示就是-1640531535.

所以LZ4的Hash函数如下:

关于LZ4的Hash函数,这个方法我不太理解,贴出来,如果有了解朋友,希望不吝赐教。org.apache.lucene.codecs.compressing.LZ4.HashTable

LZ4核心

LZ4算法的格式很简单:

Token用来控制上图中LiteralLength和MatchLength的部分。一个Token的8个字节被分为两个部分,高4位表示Literal
Length(原文长度),我们知道4bits最大只能存15,如果长度等于15,那么就需要用到Literal
length(optional)部分了。同理,低4位表示Math
Length(匹配长度),如果匹配长度等于15,则会用到Math length (optional) 。Literals表示原文。Offset表示文本处理过程中匹配部分相对当前位置的偏移。

LZ4算法还有一些特定的规则:

1、被压缩文本的最后5个byte只能用原文表示。

2、最后一次匹配的开始位置到文本末尾不能少于12个byte。

3、如果被压缩文本的长度小于13个byte,那么用原文表示会更节省空间。

编码过程

下面以一个样例来解释Lucene中LZ4

假如我们把符串aaaaabaaaaacaaaaa用LZ4来压缩。压缩的代码如下:

整个压缩过程中,核心的代码在LZ4.java中:

方法名中的参数解释如下:压缩bytes[off:off+len]到out中,最多会使用16KB的内存<这句没有理解>。Hash表ht不是线程安全的,但是可以安全地重用。

压缩的过程如下:

1、读取4个字节,表示成一个int

(final int v = readInt(bytes,
off);)

2、用Hash表得到其Hash值。

(final int h = hash(v, hashLog);)

3、记录上次映射到此Hash值在bytes数组中的位置

(ref = base + (int) hashTable.get(h);)

4、把当前的Hash值及此值在bytes数组中的位置记录到Hash表中

(hashTable.set(h, off - base);)

5、如果hash的位置发生了冲突,并且匹配成功,那么就表示找到一个可压缩的数据段了,而且这个匹配最小有4个字节(int=4bytes);否则窗口向前移动一个字节,回到第1步。

由于匹配是4个字节大小的窗口,所以如果匹配成功,那么就扩展当前匹配的成果。其代码在:LZ4.java的第241行:

// compute match length

final int matchLen = MIN_MATCH + commonBytes(bytes,
ref + MIN_MATCH, off + MIN_MATCH, limit);

我们关注这个commonBytes函数:

跟踪一下就会发现其实就是在匹配的基础上往后探路,找到最大匹配。

如果找到了可压缩片断,则按照上图的格式进行编码:

重复这一过程,直到最后把文本末尾的片断进行编码:

解码过程

压缩的结果是 [ 16, 97, 1, 0,
16, 98, 5, 0, 112, 97, 99, 97, 97, 97, 97, 97] 这个编码包含三个部分:[ 16, 97, 1, 0]   [ 16, 98, 5, 0]  [ 112, 97, 99, 97, 97, 97, 97, 97] 我们来根据编码的规则,一一解析。即解码的过程.

解析第一部分的内容:

Token=16;
Literals=[97]; offset=[1,0]

Token其实是一个byte分成两部分:前4bit表示LiteralLength,后4bit表示MatchLength.既然如此,我们把Token拆开: LiteralLength=
token>>>4 = 1; MatchLength= token&0x0F
=0由于是按4-bytes来匹配的,所以MatchLength需要加4,即MatchLength=4 由于literalLength=1<15 ,所以接下的Field是<Literals> ;即dest=[97]

接下来,读取到<offset>
Field;这个Field是两个byte按Little-Endian格式存储的一个short类型。其值为offset=(offset[0]
& 0xFF) | ((offset[1]& 0xFF)<<8)=1。既然是offset,那么肯定需要一个相对的位置。这个相对的位置就是current position,而不是0,这一点需要记住。所以offset=0是无效的。 Offset = 1的意思就是current
position-1,推而广之real position = current position –offset  . 这样的话解码所需的基本要素就齐了,那怎么解码呢?

通过LiteralLength和Literal把Literal写入到byte数组中,如下:

Depressed=[97], current
position=1 real position = current position – offset = 0. matchLength=4

这里出现了一个矛盾:Offset=1, matchLength=4, Offset<matchLength这说明Literal有重叠现象。怎么做呢?

这段代码说得非常清楚:在解码的过程中,会有两个可能,一种是上面的例子----有重叠;一种是没有重叠。有重叠的话就 “na&iuml;ve incremental
copy”。从字面上不好解释清楚,看代码,多用几个例子,比如“abababababababa”跟踪一下,就能体会到什么是“naive incremental
copy”。没有重叠的话就直接调用System.arraycopy()方法,把前面的内容复制过去就OK了。

经过上面的步骤,depressed=[97,97,97,97,97],接下来解码第二部分的内容:[
16, 98, 5, 0]

Token=16; Literals=[98]; offset=[1,0]即LiteralLength=1, MatchLength=0+4=4.
Offset=5

Offset > LiteralLength,直接调用System.arraycopy()方法。

得到depressed=[97,
97,97,97,97,98,97,97,97,97,98],看到这里,我们会发现出问题了,原数据是“aaaaabaaaaa”,到这里变成了“aaaaabaaaab”了。其实这里是没有问题的,我们看depressed的下标位置就能够明白了。

接下来处理最后的部分:[
112, 97, 99, 97, 97, 97, 97, 97]

Token=112;读取出LiteralLength= token>>>4 =7,即literals=[97, 99, 97, 97,
97, 97, 97] 然后把Literals 复制到depressed数组中,得到最终的结果:

epressed=[97,
97,97,97,97,98,97,97,97,97, 97, 99, 97, 97, 97, 97, 97]。这样解码就完毕了。

LZ4的匹配算法在Lucene是用Hash来匹配。当然我们还可以选择其它的数据结构和算法,比如Morphing  Match Chain,BinarySearch Tree,Hash Chain,2D Hash Table等等。由于LZ4算法已经到了数据压缩的领域了,已经偏离了Lucene的核心,暂时就浅尝辄止到这里了。

参考博客:

http://fastcompression.blogspot.com/p/lz4.html<可能需要翻墙>

lucene源代码学习之LZ4压缩算法在lucene中应用

时间: 2024-10-20 16:06:20

lucene源代码学习之LZ4压缩算法在lucene中应用的相关文章

Lucene学习总结之二:Lucene的总体架构

Lucene总的来说是: 一个高效的,可扩展的,全文检索库. 全部用Java实现,无须配置. 仅支持纯文本文件的索引(Indexing)和搜索(Search). 不负责由其他格式的文件抽取纯文本文件,或从网络中抓取文件的过程. 在Lucene in action中,Lucene 的构架和过程如下图, 说明Lucene是有索引和搜索的两个过程,包含索引创建,索引,搜索三个要点. 让我们更细一些看Lucene的各组件: 被索引的文档用Document对象表示. IndexWriter通过函数addD

【Todo】Lucene系统学习

之前已经写过一篇关于Lucene安装学习的文章:http://www.cnblogs.com/charlesblc/p/5980525.html 还有一篇关于Solr安装使用的文章:http://www.cnblogs.com/charlesblc/p/5981292.html 上面两篇比较偏实践和应用,开了个头:这一篇是在上面两篇基础上,针对Lucene原理再进行的学习. 参考的文章有:http://www.cnblogs.com/forfuture1978/archive/2009/12/1

Lucene入门学习

参考博客: http://blog.csdn.net/ayi_5788/article/category/6348409 分页: http://blog.csdn.net/hu948162999/article/details/41209699 1. 什么是中文分词 学过英文的都知道,英文是以单词为单位的,单词与单词之间以空格或者逗号句号隔开.而中文则以字为单位,字又组成词,字和词再组成句子.所以对于英文,我们可以简单以空格判断某个字符串是否为一个单词,比如I love China,love 和

Lucene学习笔记: 五,Lucene搜索过程解析

一.Lucene搜索过程总论 搜索的过程总的来说就是将词典及倒排表信息从索引中读出来,根据用户输入的查询语句合并倒排表,得到结果文档集并对文档进行打分的过程. 其可用如下图示: 总共包括以下几个过程: IndexReader打开索引文件,读取并打开指向索引文件的流. 用户输入查询语句 将查询语句转换为查询对象Query对象树 构造Weight对象树,用于计算词的权重Term Weight,也即计算打分公式中与仅与搜索语句相关与文档无关的部分(红色部分). 构造Scorer对象树,用于计算打分(T

lucene学习记录(一)--lucene demo的学习

敬伟大的实践出真知! 以前研究过全文检索,不过当时重点放在了使用上,而且当时重点放在了基于lucene之上的工具zoie,没有时间好好研究一下真正的实现内容.故现在闲暇时间好好看看官网,研究一下lucene这个全文检索的根.由于水平有限,很多地方比较浅显而且可能会有错误,请看官海涵,敬请指正! 本篇文章直接跳过lucene的各种介绍,援引等等,直接从lucene自带的demo开始记录. 我使用的lucene版本是4.10.2.下载地址:下载,因为我使用的Windows环境,故直接下载了zip包,

Lucene的学习及使用实验

实验一下Lucene是怎么使用的. 参考:http://www.importnew.com/12715.html (例子比较简单) http://www.yiibai.com/lucene/lucene_first_application.html (例子比较复杂) 这里也有一个例子:http://www.tuicool.com/articles/aqIZNnE 我用的版本比较高,是6.2.1版本,文档查阅: http://lucene.apache.org/core/6_2_1/core/in

Lucene的一个简单的标准测试(Lucene包基于3.5版本的)

Lucene编程一般分为:索引.分词.搜索 索引源代码: package lucene的一个标准测试; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.Date; import org.apache.lucene.anal

Lucene.Net 3.0.3如何从TokenStream中获取token对象

Lucene.Net最高版本为3.0.3,并且apache已经不再提供Lucene.Net的更新,没仔细研究过Lucene.Net的所有版本,Lucene.Net3.0.3遍历TokenStream获取Token对象,已经和以前的版本有了很大的区别,很多方法都已经删除了或者过时. 以前版本的Lucene.Net从TokenStream中获取Token时调用Next方法就行了,源代码如下 public void ReusableTokenStream2() { string testwords =

nginx源代码学习资源(不断更新)

nginx源代码学习是一个痛苦又快乐的过程,以下列出了一些nginx的学习资源. 首先要做的当然是下载一份nginx源代码,能够从nginx官方站点下载一份最新的. 看了nginx源代码,发现这是一份全然没有凝视,全然没有配置文档的代码. 如今你最希望要的是一份凝视版的nginx源代码,能够从以下的链接中下载一份: https://github.com/jianfengye/nginx-1.0.14_comment 这份凝视版源代码会不断进行更新的 好了,第一个问题, nginx的main函数在