Lucene 查询原理

摘要: # 前言 Lucene 是一个基于 Java 的全文信息检索工具包,目前主流的搜索系统Elasticsearch和solr都是基于lucene的索引和搜索能力进行。想要理解搜索系统的实现原理,就需要深入lucene这一层,看看lucene是如何存储需要检索的数据,以及如何完成高效的数据检索。

前言

Lucene 是一个基于 Java 的全文信息检索工具包,目前主流的搜索系统Elasticsearch和solr都是基于lucene的索引和搜索能力进行。想要理解搜索系统的实现原理,就需要深入lucene这一层,看看lucene是如何存储需要检索的数据,以及如何完成高效的数据检索。

在数据库中因为有索引的存在,也可以支持很多高效的查询操作。不过对比lucene,数据库的查询能力还是会弱很多,本文就将探索下lucene支持哪些查询,并会重点选取几类查询分析lucene内部是如何实现的。为了方便大家理解,我们会先简单介绍下lucene里面的一些基本概念,然后展开lucene中的几种数据存储结构,理解了他们的存储原理后就可以方便知道如何基于这些存储结构来实现高效的搜索。本文重点关注是lucene如何做到传统数据库较难做到的查询,对于分词,打分等功能不会展开介绍。

本文具体会分以下几部分:

  1. 介绍lucene的数据模型,细节可以参阅lucene数据模型一文。
  2. 介绍lucene中如何存储需要搜索的term。
  3. 介绍lucene的倒排链的如何存储以及如何实现docid的快速查找。
  4. 介绍lucene如何实现倒排链合并。
  5. 介绍lucene如何做范围查询和前缀匹配。
  6. 介绍lucene如何优化数值类范围查询。

Lucene数据模型

Lucene中包含了四种基本数据类型,分别是:

Index:索引,由很多的Document组成。
Document:由很多的Field组成,是Index和Search的最小单位。
Field:由很多的Term组成,包括Field Name和Field Value。
Term:由很多的字节组成。一般将Text类型的Field Value分词之后的每个最小单元叫做Term。

在lucene中,读写路径是分离的。写入的时候创建一个IndexWriter,而读的时候会创建一个IndexSearcher,
下面是一个简单的代码示例,如何使用lucene的IndexWriter建索引以及如何使用indexSearch进行搜索查询。

    Analyzer analyzer = new StandardAnalyzer();// Store the index in memory:Directory directory = new RAMDirectory();// To store an index on disk, use this instead://Directory directory = FSDirectory.open("/tmp/testindex");IndexWriterConfig config = new IndexWriterConfig(analyzer);IndexWriter iwriter = new IndexWriter(directory, config);Document doc = new Document();String text = "This is the text to be indexed.";doc.add(new Field("fieldname", text, TextField.TYPE_STORED));iwriter.addDocument(doc);iwriter.close();// Now search the index:DirectoryReader ireader = DirectoryReader.open(directory);IndexSearcher isearcher = new IndexSearcher(ireader);// Parse a simple query that searches for "text":QueryParser parser = new QueryParser("fieldname", analyzer);Query query = parser.parse("text");ScoreDoc[] hits = isearcher.search(query, 1000).scoreDocs;//assertEquals(1, hits.length);// Iterate through the results:for (int i = 0; i < hits.length; i++) {Document hitDoc = isearcher.doc(hits[i].doc);System.out.println(hitDoc.get("fieldname"));}ireader.close();directory.close();

从这个示例中可以看出,lucene的读写有各自的操作类。本文重点关注读逻辑,在使用IndexSearcher类的时候,需要一个DirectoryReader和QueryParser,其中DirectoryReader需要对应写入时候的Directory实现。QueryParser主要用来解析你的查询语句,例如你想查 “A and B",lucene内部会有机制解析出是term A和term B的交集查询。在具体执行Search的时候指定一个最大返回的文档数目,因为可能会有过多命中,我们可以限制单词返回的最大文档数,以及做分页返回。

下面会详细介绍一个索引查询会经过几步,每一步lucene分别做了哪些优化实现。

Lucene 查询过程

在lucene中查询是基于segment。每个segment可以看做是一个独立的subindex,在建立索引的过程中,lucene会不断的flush内存中的数据持久化形成新的segment。多个segment也会不断的被merge成一个大的segment,在老的segment还有查询在读取的时候,不会被删除,没有被读取且被merge的segement会被删除。这个过程类似于LSM数据库的merge过程。下面我们主要看在一个segment内部如何实现高效的查询。

为了方便大家理解,我们以人名字,年龄,学号为例,如何实现查某个名字(有重名)的列表。

docidnameageid
1Alice18101
2Alice20102
3Alice21103
4Alan21104
5Alan18105

在lucene中为了查询name=XXX的这样一个条件,会建立基于name的倒排链。以上面的数据为例,倒排链如下:
姓名

Alice | [1,2,3]
---- | --- | 
Alan | [4,5]
如果我们还希望按照年龄查询,例如想查年龄=18的列表,我们还可以建立另一个倒排链:

18 | [1,5]
---| --- | 
20 | [2]
21 | [3,4]

在这里,Alice,Alan,18,这些都是term。所以倒排本质上就是基于term的反向列表,方便进行属性查找。到这里我们有个很自然的问题,如果term非常多,如何快速拿到这个倒排链呢?在lucene里面就引入了term dictonary的概念,也就是term的字典。term字典里我们可以按照term进行排序,那么用一个二分查找就可以定为这个term所在的地址。这样的复杂度是logN,在term很多,内存放不下的时候,效率还是需要进一步提升。可以用一个hashmap,当有一个term进入,hash继续查找倒排链。这里hashmap的方式可以看做是term dictionary的一个index。 从lucene4开始,为了方便实现rangequery或者前缀,后缀等复杂的查询语句,lucene使用FST数据结构来存储term字典,下面就详细介绍下FST的存储结构。

FST

我们就用Alice和Alan这两个单词为例,来看下FST的构造过程。首先对所有的单词做一下排序为“Alice”,“Alan”。

  1. 插入“Alan”

  2. 插入“Alice”

这样你就得到了一个有向无环图,有这样一个数据结构,就可以很快查找某个人名是否存在。FST在单term查询上可能相比hashmap并没有明显优势,甚至会慢一些。但是在范围,前缀搜索以及压缩率上都有明显的优势。

在通过FST定位到倒排链后,有一件事情需要做,就是倒排链的合并。因为查询条件可能不止一个,例如上面我们想找name="alan" and age="18"的列表。lucene是如何实现倒排链的合并呢。这里就需要看一下倒排链存储的数据结构

SkipList

为了能够快速查找docid,lucene采用了SkipList这一数据结构。SkipList有以下几个特征:

  1. 元素排序的,对应到我们的倒排链,lucene是按照docid进行排序,从小到大。
  2. 跳跃有一个固定的间隔,这个是需要建立SkipList的时候指定好,例如下图以间隔是3
  3. SkipList的层次,这个是指整个SkipList有几层

有了这个SkipList以后比如我们要查找docid=12,原来可能需要一个个扫原始链表,1,2,3,5,7,8,10,12。有了SkipList以后先访问第一层看到是然后大于12,进入第0层走到3,8,发现15大于12,然后进入原链表的8继续向下经过10和12。
有了FST和SkipList的介绍以后,我们大体上可以画一个下面的图来说明lucene是如何实现整个倒排结构的:


有了这张图,我们可以理解为什么基于lucene可以快速进行倒排链的查找和docid查找,下面就来看一下有了这些后如何进行倒排链合并返回最后的结果。

倒排合并

假如我们的查询条件是name = “Alice”,那么按照之前的介绍,首先在term字典中定位是否存在这个term,如果存在的话进入这个term的倒排链,并根据参数设定返回分页返回结果即可。这类查询,在数据库中使用二级索引也是可以满足,那lucene的优势在哪呢。假如我们有多个条件,例如我们需要按名字或者年龄单独查询,也需要进行组合 name = "Alice" and age = "18"的查询,那么使用传统二级索引方案,你可能需要建立两张索引表,然后分别查询结果后进行合并,这样如果age = 18的结果过多的话,查询合并会很耗时。那么在lucene这两个倒排链是怎么合并呢。
假如我们有下面三个倒排链需要进行合并。


在lucene中会采用下列顺序进行合并:

  1. 在termA开始遍历,得到第一个元素docId=1
  2. Set currentDocId=1
  3. 在termB中 search(currentDocId) = 1 (返回大于等于currentDocId的一个doc),

    1. 因为currentDocId ==1,继续
    2. 如果currentDocId 和返回的不相等,执行2,然后继续
  4. 到termC后依然符合,返回结果
  5. currentDocId = termC的nextItem
  6. 然后继续步骤3 依次循环。直到某个倒排链到末尾。

整个合并步骤我可以发现,如果某个链很短,会大幅减少比对次数,并且由于SkipList结构的存在,在某个倒排中定位某个docid的速度会比较快不需要一个个遍历。可以很快的返回最终的结果。从倒排的定位,查询,合并整个流程组成了lucene的查询过程,和传统数据库的索引相比,lucene合并过程中的优化减少了读取数据的IO,倒排合并的灵活性也解决了传统索引较难支持多条件查询的问题。

BKDTree

在lucene中如果想做范围查找,根据上面的FST模型可以看出来,需要遍历FST找到包含这个range的一个点然后进入对应的倒排链,然后进行求并集操作。但是如果是数值类型,比如是浮点数,那么潜在的term可能会非常多,这样查询起来效率会很低。所以为了支持高效的数值类或者多维度查询,lucene引入类BKDTree。BKDTree是基于KDTree,对数据进行按照维度划分建立一棵二叉树确保树两边节点数目平衡。在一维的场景下,KDTree就会退化成一个二叉搜索树,在二叉搜索树中如果我们想查找一个区间,logN的复杂度就会访问到叶子结点得到对应的倒排链。如下图所示:

如果是多维,kdtree的建立流程会发生一些变化。
比如我们以二维为例,建立过程如下:

  1. 确定切分维度,这里维度的选取顺序是数据在这个维度方法最大的维度优先。一个直接的理解就是,数据分散越开的维度,我们优先切分。
  2. 切分点的选这个维度最中间的点。
  3. 递归进行步骤1,2,我们可以设置一个阈值,点的数目少于多少后就不再切分,直到所有的点都切分好停止。

下图是一个建立例子:

BKDTree是KDTree的变种,因为可以看出来,KDTree如果有新的节点加入,或者节点修改起来,消耗还是比较大。类似于LSM的merge思路,BKD也是多个KDTREE,然后持续merge最终合并成一个。不过我们可以看到如果你某个term类型使用了BKDTree的索引类型,那么在和普通倒排链merge的时候就没那么高效了所以这里要做一个平衡,一种思路是把另一类term也作为一个维度加入BKDTree索引中。

如何实现返回结果进行排序聚合

通过之前介绍可以看出lucene通过倒排的存储模型实现term的搜索,那对于有时候我们需要拿到另一个属性的值进行聚合,或者希望返回结果按照另一个属性进行排序。在lucene4之前需要把结果全部拿到再读取原文进行排序,这样效率较低,还比较占用内存,为了加速lucene实现了fieldcache,把读过的field放进内存中。这样可以减少重复的IO,但是也会带来新的问题,就是占用较多内存。新版本的lucene中引入了DocValues,DocValues是一个基于docid的列式存储。当我们拿到一系列的docid后,进行排序就可以使用这个列式存储,结合一个堆排序进行。当然额外的列式存储会占用额外的空间,lucene在建索引的时候可以自行选择是否需要DocValue存储和哪些字段需要存储。

Lucene的代码目录结构

介绍了lucene中几个主要的数据结构和查找原理后,我们在来看下lucene的代码结构,后续可以深入代码理解细节。lucene的主要有下面几个目录:

  1. analysis模块主要负责词法分析及语言处理而形成Term。
  2. codecs模块主要负责之前提到的一些数据结构的实现,和一些编码压缩算法。包括skiplist,docvalue等。
  3. document模块主要包括了lucene各类数据类型的定义实现。
  4. index模块主要负责索引的创建,里面有IndexWriter。
  5. store模块主要负责索引的读写。
  6. search模块主要负责对索引的搜索。
  7. geo模块主要为geo查询相关的类实现
  8. util模块是bkd,fst等数据结构实现。

最后

本文介绍了lucene中的一些主要数据结构,以及如何利用这些数据结构实现高效的查找。我们希望通过这些介绍可以加深理解倒排索引和传统数据库索引的区别,数据库有时候也可以借助于搜索引擎实现更丰富的查询语意。除此之外,做为一个搜索库,如何进行打分,query语句如何进行parse这些我们没有展开介绍,有兴趣的同学可以深入lucene的源码进一步了解。

原文链接

干货好文,请关注扫描以下二维码:



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/521998.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mac解压rar命令_苹果mac电脑上很好用的免费压缩软件?ezip压缩软件分享

在开始之前&#xff0c;先问问用苹果电脑的大家一个问题&#xff0c;有没有遇到过这种情况呢&#xff1f;就是好不容易在网上找到了需要的素材&#xff0c;然后下载回来后发现&#xff0c;想解压却解压不了。因为mac系统自带的压缩工具是不支持rar格式的&#xff0c;而在网上很…

python第四章单元测试_智慧树APPPython语言应用第四单元章节测试答案

有关风能资源&#xff0c;下列描述正确的是? 受地球自转和公转影响|太阳能的一种转化形式|储量大、分布广|能量密度低【判断题】古代官式建筑中以重檐歇山顶的规格最高,如故宫太和殿。() (5.0分)【单选题】当发生胆红素脑病时A. 血清胆红素≥85μmol/LB. 血清胆红素≤171μmol…

日常软件下载官网

官网链接3322下载站http://www.3322.cc/pc6下载http://www.pc6.com/服务器常用软件https://www.zzidc.com/main/softwares/showSoftwares

如何从机器学习数据中获取更多收益

摘要&#xff1a; 本文讲解一些关于机器学习数据集的小技巧&#xff0c;分享个人经验&#xff0c;可供读者参考。对于深度学习而言&#xff0c;合适的数据集以及合适的模型结构显得至关重要。选择错误的数据集或者错误的模型结构可能导致得到一个性能不佳的网络模型&#xff0c…

CSDN×易观算法大赛火热进行中~

伴随着5G、物联网与大数据形成的后互联网格局的逐步形成&#xff0c;日益多样化的用户触点、庞杂的行为数据和沉重的业务体量也给我们的数据资产管理带来了不容忽视的挑战。为了建立更加精准的数据挖掘形式和更加智能的机器学习算法&#xff0c;对不断生成的用户行为事件和各类…

该放弃正在堕落的“RNN和LSTM”了

摘要&#xff1a; 随着技术的发展&#xff0c;作者觉得是时候放弃LSTM和RNN了&#xff01;到底为什么呢&#xff1f;来看看吧&#xff5e;递归神经网络&#xff08;RNN&#xff09;&#xff0c;长期短期记忆&#xff08;LSTM&#xff09;及其所有变体&#xff1a;现在是放弃它们…

textarea支持a标签_微慕小程序开源版A标签优化说明

微慕WordPress小程序所有版本里&#xff0c;对于文章详情里文字内容的解析&#xff0c;都是通过开源组件&#xff1a;wxParse&#xff1b;不过这个组件已经停止维护&#xff0c;微慕小程序在这个组件上做了一些优化&#xff0c;让文章里文字在小程序显示更加完美。但&#xff0…

weigm怎么下载_彩七官网下载地址|官网

游戏介绍彩七官网下载地址|官网 是一款非常火爆的盒游戏&#xff0c;这款游戏非常受欢迎&#xff0c;因为它的玩法非常的丰富&#xff0c;在彩七官网下载地址|官网你可以自由建造任何事物&#xff0c;你可以打造一个属于你自己的世界&#xff0c;这不仅仅是一款沙盒游戏&#x…

七本书籍带你打下机器学习和数据科学的数学基础

摘要&#xff1a; 本文主要介绍七本关于机器学习和数据科学数学基础的经典教材&#xff0c;是一份不可多得的书单整理。大多数人学习数据科学的重心放在编程上面&#xff0c;然而&#xff0c;要真正精通数据科学的话是不能够忽视数据科学背后的数据基础。本篇文章&#xff0c;将…

oracle数据库字符编码utf-8改为ZHS16GBK

首先查看服务端字符集 select * from v$nls_parameters where parameter NLS_CHARACTERSET NLS_CHARACTERSET WE8MSWIN1252然后用sys用户进行修改 >sqlplus >conn /as sysdba >输入口令: >shutdown immediate; >startup mount; >ALTER SESSION SET SQL_T…

奇葩面试官让我回去等通知!看我怎么虐他!

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 农翻身刘鑫来源 | 码农翻身后记&#xff1a;已经发了两篇漫画了&#xff0c;一个是《我才是世界上最好的语言》&#xff0c;还有一个是《HTTP之大明邮差》&#xff0c;前两篇主要讲技术&#xff0c;这一篇是娱乐性质&#xf…

阿里:千亿交易背后的0故障发布

摘要&#xff1a; 阿里巴巴千亿交易背后&#xff0c;如何尽量避免发布故障&#xff1f;在面对实际运维过程中遇到的问题该如何解决&#xff1f;近日&#xff0c;在刚刚结束的 GOPS深圳站大会上&#xff0c;阿里巴巴运维技术专家少荃&#xff0c;给我们带来了解决方案和思路。前…

什么是python之禅_【Python面试】你了解什么是 Python 之禅么?

公众号新增加了一个栏目&#xff0c;就是每天给大家解答一道Python常见的面试题&#xff0c;反正每天不贪多&#xff0c;一天一题&#xff0c;正好合适&#xff0c;只希望这个面试栏目&#xff0c;给那些正在准备面试的同学&#xff0c;提供一点点帮助&#xff01;小猿会从最基…

mesos marathon mysql_Mesos以及Marathon安装总结

安装了将近一周的环境了&#xff0c;终于把Mesos以及Marathon给安装上了&#xff0c;我指的离线安装。策略1&#xff1a;严格的按照官网的流程&#xff1a;找一台能够上网的机器&#xff0c;通过yum install/update --downloadonly --downloaddir/opt/XXX但是所有的yum下载都不…

SonarQube启动报错:WrapperSimpleApp: Encountered an error running main: java.nio.file.AccessDeniedExcepti

SonarQube启动报错 可以查看日志&#xff0c;一般日志会记录错误的详细内容 执行以下命令&#xff0c;查看日志&#xff1a; cd /opt/sonarqube-6.7.6/logs tail -n 100 sonar.log定位到问题的详细信息&#xff1a; WrapperSimpleApp: Encountered an error running main: ja…

华为麒麟990芯片发布;谷歌宣布开源创新隐私保护技术;阿里20亿美元全资收购网易考拉;中国联通将设立100亿5G创新基金……...

关注并标星星CSDN云计算极客头条&#xff1a;速递、最新、绝对有料。这里有企业新动、这里有业界要闻&#xff0c;打起十二分精神&#xff0c;紧跟fashion你可以的&#xff01;每周三次&#xff0c;打卡即read更快、更全了解泛云圈精彩newsgo go go 东风新款电动车菱智M5 EV上市…

C++继承和组合——带你读懂接口和mixin,实现多功能自由组合

摘要&#xff1a; 本文详细介绍了C继承的三种方式和相关重要概念&#xff0c;整理了众多继承与组合中的注意问题。在C继承存在不安全的默认实现&#xff0c;非虚函数的覆盖&#xff0c;多重继承的函数名冲突、菱形继承等众多问题下&#xff0c;如何实现多个功能的自由组合&…

python3新式类_python新式类和旧式类区别

python的新式类是2.2版本引进来的&#xff0c;我们可以将之前的类叫做经典类或者旧式类。为什么要在2.2中引进new style class呢&#xff1f;官方给的解释是&#xff1a;为了统一类(class)和类型(type)。在2.2之前&#xff0c;比如2.1版本中&#xff0c;类和类型是不同的&#…

如何在阿里云•对象存储OSS托管用户域名的https证书

摘要&#xff1a; OSS服务支持将用户域名绑定到OSS域名&#xff0c;并为此用户域名提供HTTPS证书托管服务。以满足用户使用自己的域名也能支持HTTPS安全加密的访问方式传输数据。面向人群您已经拥有了自己的域名。您已将或准备将自己的域名绑定到OSS域名上&#xff0c;并且使用…

win10系统VMware Workstation与Device/Credential Guard不兼容怎么办

解决方法&#xff1a; 打开本电脑-》管理-》服务和应用程序-》服务下找到如下图的HV 主机服务&#xff0c;双击选择禁用。 或者 转到“ 控制面板” >“ 卸载程序” >“ 打开或关闭Windows功能”以关闭Hyper-V。 步骤二&#xff1a;通过命令关闭Hyper-V&#xff08;控制…