ES相关Lucene知识

1. Lucene

lucene的学习目的主要是为了更好的理解ES的原理,重点理解两个知识点:

倒排索引

数据分段

1.1 倒排索引

img

假设现在有10W+份word文档,让你做个web页面,给出关键词能快速搜索结果,你会怎么做?那至少有3种方案,

  1. 顺序扫描,每次检测文档中是否包含关键词,包含则加入结果列表,不包含继续查找下一个,直到找完为止。
  2. 将文档内容导入数据库,用SQL的like关键词搜索。
  3. 用Lucene做全文检索。

你会选哪种?首先顺序扫描在数据量大的时候太慢,数据量比较少的话可行;导入数据库数据量太大用like会引起全表扫描也会很慢,那当然是第三种,因为搜索引擎Lucene就是专门为这种场景设计的:大量的,非结构化数据的快速搜索

1.1.1 全文检索原理

全文检索原理很简单,就拿新华字典做比喻,假设字典没有索引页,你要找一个字的解释你就得一页一页翻,直到找到为止,这效率可想而知

现在有了索引页,就可以根据拼音首字母或偏旁部首快速定位到目标字在哪一页,这个索引页,就是全文检索核心。

简而言之:在非结构化数据中,将一部分结构化信息抽取出来,重新组织,然后针对这部分有结构的数据进行索引建立,从而达到加速查询的目的。那么

  1. 索引存啥?怎么存?(对应字典的索引页部分)
  2. 数据怎么存?(对应字典非索引页部分)

1.1.2 反向索引

在Lucene 中,关键词就是Term(比如英文里面的一个单词,中文的词语),字典就是Term的集合。下图是Lucene 的索引的底层存储结构,左边是字典,右边保存的是包含左边字符串的文档编码链表,称之为倒排表

img

上面这种就叫做反向索引,是相对正向索引而言的:

  • 正向索引:从文档中查找关键词,关系型数据库使用的是正向索引
  • 反向索引:从关键词查找文档,搜索引擎lucene使用的是反向索引

1.2 数据分段

搞定了索引之后,再来看看数据是怎么存储的,试想一下如果所有数据(新华字典中所有的字)都存在一个文件中,如果数据有更新或者删除(比如新增加一个字或者删除一个字),那么所有的索引都需要全量重新创建,这种方式在数据量很大时效率很低,并且由于创建一次索引的成本很高,所以对数据的更新不能过于频繁,也就不能保证时效性。

Lucene在搜索中引入了段(Segment)的概念(将一个索引文件拆分为多个子文件,每个子文件叫作段),每个段都是一个独立的可被搜索的数据集,并且段具有不变性,一旦索引的数据被写入硬盘,就不可再修改。

在分段的思想下,对数据写操作的过程如下。

  • 新增。当有新的数据需要创建索引时,由于段的不变性,所以选择新建一个段来存储新增的数据。
  • 删除。当需要删除数据时,由于数据所在的段只可读,不可写,所以Lucene在索引文件下新增了一个.del的文件,用来专门存储被删除的数据id。当查询时,被删除的数据还是可以被查到的,只是在进行文档链表合并时,才把已经删除的数据过滤掉。被删除的数据在进行段合并时才会真正被移除
  • 更新。更新的操作其实就是删除和新增的组合,先在.del文件中记录旧数据,再在新段中添加一条更新后的数据。

img

1.2.1 段不可变的优点

段不变性的优点如下:

  • 不需要锁。因为数据不会更新,所以不用考虑多线程下的读写不一致情况。从段的不变性角度来说,确实不用考虑多线程问题。ES乐观锁是从什么角度说的呢?其实lucene虽然数据写入段是不可变的,删除记录和新增的组合实现了修改,再次引入了锁的概念。
  • 可以常驻内存。段在被加载到内存后,由于具有不变性,所以只要内存的空间足够大,就可以长时间驻存,大部分查询请求会直接访问内存,而不需要访问磁盘,使得查询的性能有很大的提升。
  • 缓存友好。在段的生命周期内始终有效,不需要在每次数据更新时被重建。
  • 增量创建。分段可以做到增量创建索引,可以轻量级地对数据进行更新,由于每次创建的成本很低,所以可以频繁地更新数据,使系统接近实时更新。

1.2.2 段不可变的缺点

  • 当对数据进行删除时,旧数据不会被马上删除,而是在.del文件中被标记为删除。而旧数据只能等到段更新时才能真正被移除,这样会有大量的空间浪费
  • 更新。更新数据由删除和新增这两个动作组成。若有一条数据频繁更新,则会有大量的空间浪费
  • 由于索引具有不变性,所以每次新增数据时,都需要新增一个段来存储数据。当段的数量太多时,对服务器的资源(如文件句柄)的消耗会非常大,查询的性能也会受到影响。
  • 在查询后需要对已经删除的旧数据进行过滤,这增加了查询的负担

1.2.3 延迟写策略

为了提升写的性能,Lucene并没有每新增一条数据就增加一个段,而是采用延迟写的策略,每当有新增的数据时,就将其先写入内存中,然后批量写入磁盘中。若有一个段被写到硬盘,就会生成一个提交点,提交点就是一个用来记录所有提交后的段信息的文件。一个段一旦拥有了提交点,就说明这个段只有读的权限,失去了写的权限

相反,当段在内存中时,就只有写数据的权限,而不具备读数据的权限,所以也就不能被检索了。从严格意义上来说,Lucene或者Elasticsearch并不能被称为实时的搜索引擎,只能被称为准实时的搜索引擎。

写索引的流程如下:

新数据被写入时,并没有被直接写到硬盘中,而是被暂时写到内存中。Lucene默认是一秒钟,或者当内存中的数据量达到一定阶段时,再批量提交到磁盘中,当然,默认的时间和数据量的大小是可以通过参数控制的。通过延时写的策略,可以减少数据往磁盘上写的次数,从而提升整体的写入性能。如下图所示。

img

在达到触发条件以后,会将内存中缓存的数据一次性写入磁盘中,并生成提交点。清空内存,等待新的数据写入。

img

1.2.4 分段合并

合并原因:虽然分段比每次都全量创建索引有更高的效率,但由于在每次新增数据时都会新增一个段,所以经过长时间的积累,会导致在索引中存在大量的段,当索引中段的数量太多时,不仅会严重消耗服务器的资源,还会影响检索的性能。

因为索引检索的过程是:查询所有段中满足查询条件的数据,然后对每个段里查询的结果集进行合并,所以为了控制索引里段的数量,我们必须定期进行段合并操作。但是如果每次合并全部的段,则将造成很大的资源浪费,特别是"大段"的合并。

所以Lucene现在的段合并思路是:根据段的大小先将段进行分组,再将属于同一组的段进行合并。但是由于对超级大的段的合并需要消耗更多的资源,所以Lucene会在段的大小达到一定规模,或者段里面的数据量达到一定条数时,不会再进行合并。所以Lucene的段合并主要集中在对中小段的合并上,这样既可以避免对大段进行合并时消耗过多的服务器资源,也可以很好地控制索引中段的数量。

1.2 总结

反向索引:Lucene 采用了基于反向索引的设计原理,可以非常高效地实现文本查找

数据分段:在底层采用了数据分段的存储模式,分段是只读的,使它在读写时几乎完全避免了锁的出现,大大提升了读写性能。

转载自文章5分钟了解搜索引擎Lucene的原理

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值