看Lucene源码必须知道的基本规则和算法

  上中学的时候写作文,最喜欢的季节我都是写冬天。虽然是因为写冬天的人比较少,那时确实也是对其他季节没有什么特殊的偏好,反而一到冬天,自己皮肤会变得特别白。但是冬天啊,看到的只有四季常青盆栽:瓜栗(就是发财树,好吧,算我矫情,反正我不喜欢这个名字),绿萝,永远看不到它开花的巴西铁,富贵竹,散尾葵……过年的时候家里的杜鹃就开花了,零星的几朵小花儿更突显了这个季节的凄凉。红掌,蝴蝶兰总是美美的在那里,开不败却看不到生机。插到水里的勿忘我,洋桔梗,看到他们也只会联想到过几天他们会枯萎的命运。春天来了,先是迎春花,然后是桃花,玉兰。到了四月,红叶碧桃,紫荆,樱花,紫叶李,垂丝海棠……最喜欢丁香花的味道~~再过几日,郁金香和牡丹也该开了。桃之夭夭,灼灼其华。果然,阳光下这些花儿是流光溢彩的。人生的悲哀不是短暂的快乐过后无尽的痛苦,而是从来没让自己快乐过。想想小鲜肉看的《熊出没-雪岭熊风》电影,熊二没有再次遇到团子之前的魂儿不守舍,与团子经历过精彩之后,虽然别人什么都不记得了,所有的场景回到了最初,熊二心里却是满足和平静。就像这些花儿,虽然是花开不多时,但怒放过的青春总好过冬青一日和一生毫无区别(中学作文里还总是在赞扬它冬天还是绿的呢[此处有表情])。大概现在和中学的时候最大的区别,就是那时候的人生观更多的是受父母的影响。父母都是医生,铁饭碗,稳定是一成不变的追求。离父母越来越远,活得越来越像自己,才发现自己的人生需要冬天的期待与思考,春天花的妖娆,夏天叶的茂盛,秋天果实的沉重。谁规定的第一个季节是春天?我的人生第一个季节就不是

  下面介绍一些Lucene使用基本规则和算法。这些规则和算法的选择,都和Lucene和支持TB级的倒排索引有关。

  前缀后缀规则(Prefix+Suffix):在Lucene的反向索引中,要保存词典的信息,所有的词再词典中是按照字典顺序进行排列的,然后词典中包含了文档中的几乎所有的词,并且有的词还是很长的,这样索引文件会非常的大,所谓前缀后缀规则,就是某个词和前一个词有共同的前缀的时候,后面的词仅仅保存前缀在词中的偏移(offset),和剩下的部分(后缀)。

  比如:北京天安门  这个词词典里通常都会包含北京  天安门  北京天安门  这三个词。北京和北京天安门由于前缀相同,在字典表里会相邻存储,两个词存成  北京2天安门   ,这样存比北京北京天安门   省空间。

  差值规则(Delta):在lucene的反向索引中,需要保存很多整形数字的信息,比如文档ID号,比如词在文档中的位置等等。整形数字是以可变长整型的格式存储的。随着数值的增大,每个数字占用的比特位增多。所谓差值规则就是先后保存两个整数的时候,后面的整数仅仅保存和前面整数的差即可。多唠叨两句:因为看到有的哥哥们定义数据库字段的时候总是想都不想就用varchar,MD5的结果也用varchar[汗]。MD5的结果长度是固定的,没有必要用varchar来节省空间。定长的char效率会高些。

  LZ4算法(Realtime Compression Algorithm):在操作系统(linux/freeBSD),文件系统(OpenZFS),大数据(Hadoop),搜索引擎(Lucene/solr),数据库(Hbase)等中都可以看到它的身影,很通用。压缩/解压速度快。

  跳跃表规则(Skip list):跳跃表是一种数据结构。额~~,要不能用几句话把它介绍明白,真不好意思说自己有那么多算法专利。首先使用跳跃表的前提是因为搜索引擎的索引数据是高度有序的。打个比方:我从北京回老家青州市可以做北京南到青岛的动车或者高铁。它们的路线是一样的,后者贵100块钱。贵在哪里呢?后者停的站少,就是跳站了。有的高铁到青州市不停。我只能在前一站淄博或者后一站潍坊下车,然后坐慢车去青州市。跳跃表就是这个原理。所有的搜索数据存在一个链表里,这就是慢车(最传统的绿皮车)。然后新加一个链表,存的数据中间有间隔(K字头车)。这时候我不得不说一个原则:所有原来的时间复杂度是delta(找这个符号比较费劲,我就直接用英文了,记住它是很有好处的,去米国总免不了和这个航空公司打交道~~) n的算法,期待的终极优化后的结果基本都是 delta  log n。所以只有两层的话,时间复杂度是达不到要求的。怎样达到要求呢?最终要形成一棵树。怎么形成一棵树呢?加层呗。加大跳站的间隔,T字头车,D字头车,G字头车。一直到中间是所有的站,形成了一个root。树形结构就形成了。时间复杂度变成了delta log n[耶][耶] Lucene3.0之前很多地方使用这种数据结构来提高查找速度。但是因为它对模糊查询的支持不太好,现在Lucene改用FST了。

  关于delta再多唠叨两句:它是希腊语的第四个字母,大写是△。我这么懒,不愿意去拷贝一个小写字母到这里,大写字母打出来也是因为我直接改用日语输入法,打个[三角形]出来的~~。上面提到差值规则和时间复杂度都用到了delta。因为在数学或者物理中大写的Δ用来表示增量符号。而小写通常在高等数学中用于表示变量或者符号。所以差值规则里用到了它大写的含义,时间复杂度里用到了它小写的含义。

  有限自动机算法(FST,Finite State Transducer):通过输入有序字符串构建最小有向无环图。通过共享前缀来节省空间,内存存放前缀索引,磁盘存放后缀词块。Lucene的源码中可以看到它的具体实现。

  Lucene之所以有那么频繁的版本升级,我以前还专门追剧似的关心这个升级,是因为这里面有一个问题的发生与解决的过程,举个简单的例子:在Windows系统中一个文件夹只能存放2W多个文件,在1W多个文件以后写入速度会急剧下降,Lucene这样处理TB级数据的系统更要考虑数据量和性能的关系和权衡。

  有限自动机是Lucene的核心查找算法,理解需要一定的时间。下面介绍Lucene的打分相关规则,这部分很容易理解。

  文档权重(Document boost):在索引时给某个文档设置的权重值。

  域权重(Field boost):在查询的时候给某个域设置的权重值。

  调整因子(Coord):基于文档中包含查询关键词个数计算出来的调整因子。一般而言,如果一个文档中相比其它的文档出现了更多的查询关键词,那么其值越大。

  逆文档频率(Inerse document frequency):基于Term的一个因子,存在的意义是告诉打分公式一个词的稀有程度。其值越低,词越稀有(这里的值是指单纯的频率,即多少个文档中出现了该词;而非指Lucene中idf的计算公式)。打分公式利用这个因子提升包含稀有词文档的权重。

  长度归一化(Length norm):基于域的一个归一化因子。其值由给定域中Term的个数决定(在索引文档的时候已经计算出来了,并且存储到了索引中)。域越的文本越长,因子的权重越低。这表明Lucene打分公式偏向于域包含Term少的文档。

  词频(Term frequency):基于Term的一个因子。用来描述给定Term在一个文档中出现的次数,词频越大,文档的得分越大。

  查询归一化因子(Query norm):基于查询语句的归一化因子。其值为查询语句中每一个查询词权重的平方和。查询归一化因子使得比较不同查询语句的得分变得可行,当然比较不同查询语句得分并不总是那么易于实现和可行的。

  如需转载,请注上我的原文链接: http://www.cnblogs.com/xiexj/p/6683439.html  谢谢哦~~

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据魔术师

干货 | 十分钟掌握禁忌搜索算法求解带时间窗的车辆路径问题(附C++代码和详细代码注释)

一 什么是禁忌搜索算法? 禁忌搜索算法(Tabu Search Algorithm,简称TS)起源于对于人类记忆功能的模仿,是一种亚启发式算法(meta-...

8487
来自专栏PPV课数据科学社区

"数学之美"系列五——简单之美:布尔代数和搜索引擎的索引

建立一个搜索引擎大致需要做这样几件事:自动下载尽可能多的网页;建立快速有效的索引;根据相关性对网页进行公平准确的排序。我们在介绍 Google Page Ran...

2623
来自专栏从流域到海域

《笨办法学Python》 第27课手记

《笨办法学Python》 第27课手记 本节课讲逻辑运算(即布尔运算),对于学过数字电路或者离散数学的人来说非常简单,甚至不需要去刻意记忆真值表。 逻辑运算只有...

21110
来自专栏小樱的经验随笔

鸽巢原理(抽屉原理)的详解

抽屉原理 百科名片 ? 桌上有十个苹果,要把这十个苹果放到九个抽屉里,无论怎样放,我们会发现至少会有一个抽屉里面放两个苹果。这一现象就是我们所说的“抽屉原理”...

3827
来自专栏Hongten

Lucene学习总结之一:全文检索的基本原理

根据http://lucene.apache.org/java/docs/index.html定义:

7373
来自专栏数据分析

[数据分析工具] Pandas 功能介绍(二)

条件过滤 我们需要看第一季度的数据是怎样的,就需要使用条件过滤 ? 体感的舒适适湿度是40-70,我们试着过滤出体感舒适湿度的数据 ? 最后整合上面两种条件,在...

3636
来自专栏机器之心

资源 | 十五分钟完成Regex五天任务:FastText,语料库数据快速清理利器

43211
来自专栏算法+

快速双边滤波 附完整C代码

很早之前写过《双边滤波算法的简易实现bilateralFilter》。 当时学习参考的代码来自cuda的样例。 相关代码可以参阅: https://github...

1.2K10
来自专栏coolblog.xyz技术专栏

科普:String hashCode 方法为什么选择数字31作为乘子

某天,我在写代码的时候,无意中点开了 String hashCode 方法。然后大致看了一下 hashCode 的实现,发现并不是很复杂。但是我从源码中发现了一...

61919
来自专栏java 成神之路

高亮标红

2938

扫码关注云+社区