Lucence底层原理浅析
Lucence简介
Lucence是全Java实现的,前几年实现了开源,性能较高,而且支持分词、各种查询(正则、模糊、前缀)、关键词高亮等。
Lucence整体步骤
生成文档—>建索引库—>解析查询—>检索并返回
(1)原始数据即待索引文件,经过Tokenizer进行分词处理,去掉停用词(如"a",“the”,“an”,“啊”,“了"等),英文单词还需要Stemming和lemmatization使词汇成为词根形式(如"cars"变为"car”,“drove"变为"drive”)
(2)将词法处理后的结果创建索引,索引原理是倒排索引,创建形式是词典—>倒排表
(3)将创建好的索引放入内存
(4)将内存中的索引信息放入磁盘,保证索引的安全
(a)查询语句也经过Tokenizer进行分词,去掉停用词,英文单词也需要Stemming和lemmatization使词汇成为词根形式
(b)将一个个词放入语法树中进行语法分析(lucene AND learned NOT hadoop形成的语法树如下:)
©根据语法树,搜索索引
(d)通过索引存储将索引读入到内存
(e) 利用查询树搜索索引(先找到lucene这个词所在的文档id,然后找到learned所在的文档id,将两个文档id链取并,然后找到hadoop所在的文档id,然后将二者取非,从而得到结果)
(f) 将搜索到的结果文档对查询的相关性进行排序
(g) 返回查询结果给用户
Lucence词典结构
Lucence采用Finite State Transducers数据结构做词典存储(FST),这种数据结构是一种类似有限状态机、最小有向无环图的形式,可以共享前缀和后缀,在存储时可以节省空间,但是要求输入有序。
倒排表究竟是什么
倒排表实际上就是存储文档id的链表,对应词典的词。
如上所示,Term是原始的词,已经去重并且去掉了停用词,也规范化了词
Document Frequency是在几篇文档中出现过这个词
链接到出现该词的文档id,并统计在该文档中出现的次数
列式存储DocValues
除了基本的查询,还会涉及对检索结果的分类、排序、数字计算等,这时就需要文档号—>值的映射
DocValues其实是Lucene在构建索引时,会额外建立一个有序的基于document—>field value的映射列表
为什么要用DocValues ?
利用倒排索引模式来达到快速检索的目的,就是建立词典—>文档id列表的映射,然后在搜索时,通过类似hash算法,来快速定位到一个搜索关键词,然后读取其的文档id集合,这就是倒排索引的核心思想。但是如果需要对数据做一些聚合操作,比如排序,分组时,lucene内部会遍历提取所有出现在文档集合
的排序字段然后再次构建一个最终的排好序的文档集合list,这个步骤的过程全部维持在内存中操作,而且如果排序数据量巨大的话,非常容易就造成solr内存溢出和性能缓慢。
所以,在构建索引时,Lucence会对开启docvalues的字段,额外构建一个已经排好序的文档到字段级别的一个列式存储映射,它减轻了在排序和分组时,对内存的依赖,而且大大提升了这个过程的性能,当然它也会耗费的一定的磁盘空间。
什么时候用DocValues?
1,需要聚合的字段,包括sort,agg,group,facet等
2,需要提供函数查询的字段
3,需要高亮的字段,这个确实能加速,但是散仙并不建议把高亮放在服务端程序做,建议放在前端实现,不容易出错而且总体性能比服务端高
4,需要参与自定义评分的字段
如果只是简单的查询,可以选择关闭docvalues,这样可以节省一定的磁盘空间.
问题:
ElasticSearch是基于Lucence的一个搜索引擎。ES也采用了DocValue,并且功能强大,在一些聚合功能上:统计(sum,min,max,avg,percent,cardinality)、分组(日期直方图,地理位置分区,分组)、基于聚合再聚合(基于各个分组的平均值求最大值)
但是,ES所做的聚合只是单机聚合,由于分区大小的限制,所以返回的结果并不是准确的,但是往往前几个是准确的,这也就是为什么网站总是返回前几的搜索结果。