lucene

Lucene段概念

2019-04-23  本文已影响0人  KhaosYang

分段存储

在早期的全文检索中为整个文档集合建立了一个很大的倒排索引,并将其写入磁盘中,如果索引有更新,就需要重新全量创建一个索引来替换原来的索引。这种方式在数据量很大时效率很低,并且由于创建一次索引的成本很高,所以对数据的更新不能过于频繁,也就不能保证时效性。
现在,在搜索中引入了段的概念(将一个索引文件拆分为多个子文件,则每个子文件叫作段),每个段都是一个独立的可被搜索的数据集,并且段具有不变性,一旦索引的数据被写入硬盘,就不可再修改。

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

段不变性的优点如下:

  • 不需要锁。因为数据不会更新,所以不用考虑多线程下的读写不一致情况。
  • 可以常驻内存。段在被加载到内存后,由于具有不变性,所以只要内存的空间足够大,就可以长时间驻存,大部分查询请求会直接访问内存,而不需要访问磁盘,使得查询的性能有很大的提升。
  • 缓存友好。在段的声明周期内始终有效,不需要在每次数据更新时被重建。
  • 增量创建。分段可以做到增量创建索引,可以轻量级地对数据进行更新,由于每次创建的成本很低,所以可以频繁地更新数据,使系统接近实时更新。

段不变性的缺点如下:

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

为了提升写的性能,Lucene并没有每新增一条数据就增加一个段,而是采用延迟写的策略,每当有新增的数据时,就将其先写入内存中,然后批量写入磁盘中。若有一个段被写到硬盘,就会生成一个提交点,提交点就是一个用来记录所有提交后的段信息的文件。一个段一旦拥有了提交点,就说明这个段只有读的权限,失去了写的权限;相反,当段在内存中时,就只有写数据的权限,而不具备读数据的权限,所以也就不能被检索了。从严格意义上来说,Lucene或者Elasticsearch并不能被称为实时的搜索引擎,只能被称为准实时的搜索引擎。

写索引的流程如下。

段合并策略

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

因为索引检索的过程是:查询所有段中满足查询条件的数据,然后对每个段里查询的结果集进行合并,所以为了控制索引里段的数量,我们必须定期进行段合并操作。但是如果每次合并全部的段,则将造成很大的资源浪费,特别是“大段”的合并。
所以Lucene现在的段合并思路是:根据段的大小先将段进行分组,再将属于同一组的段进行合并。但是由于对超级大的段的合并需要消耗更多的资源,所以Lucene会在段的大小达到一定规模,或者段里面的数据量达到一定条数时,不会再进行合并。所以Lucene的段合并主要集中在对中小段的合并上,这样既可以避免对大段进行合并时消耗过多的服务器资源,也可以很好地控制索引中段的数量。

段合并的主要参数如下。

段合并相关的动作主要有以下两个:

在段合并前对段的大小进行了标准化处理,通过logMergeFactorSegmentSize
计算得出,其中,MergeFactor表示一次合并的段的数量,Lucene默认该数量为10;SegmentSize表示段的实际大小。通过上面的公式计算后,段的大小更加紧凑,对后续的分组更加友好。

段分组的步骤如下:

这样做的好处如下。

  • 增加段合并的概率,避免由于段的大小参差不齐导致段难以合并。
  • 简化了查找的逻辑,使代码的运行效率更高。

在找到满足条件的mergeFactor个段时,就需要开始合并了。但是在满足合并条件的段大于mergeFactor时,就需要进行多次合并,也就是说每次依然选择mergeFactor个段进行合并,直到该分组的所有段合并完成,再进行下一分组的查找合并操作。

通过上述几步,如果找到了满足合并要求的段,则将会进行段的合并操作。因为索引里面包含了正向信息和反向信息,所以段合并的操作分为两部分:一个是正向信息合并,例如存储域、词向量、标准化因子等;一个是反向信息的合并,例如词典、倒排表等。在段合并时,除了需要对索引数据进行合并,还需要移除段中已经删除的数据。

上一篇 下一篇

猜你喜欢

热点阅读