初识ElasticSearch - 06 动态更新索引

初识ElasticSearch - 06 动态更新索引,第1张

初识ElasticSearch - 06 动态更新索引

文章目录
    • 1. 建立索引
    • 2. 倒排索引的不变性
    • 3. 动态更新索引
      • 3.1 动态更新索引原理
      • 3.2 新增文档
      • 3.3 删除和更新文档

本系列往期文章:

初识ElasticSearch - 01 认识搜索引擎
初识ElasticSearch - 02 ElasticSearch的基本概念
初识ElasticSearch - 03 集群的内部原理
初识ElasticSearch - 04 分布式文档存储
初识ElasticSearch - 05 ElasticSearch索引原理

1. 建立索引

给定一个文档集合(这个集合中的文档是不变的),索引是如何建立起来的呢?

首先在内存里维护一个倒排索引,当内存占满后,将内存数据写入磁盘临时文件,第二阶段对临时文件进行合并形成最终索引。

① 从磁盘读取文档,对文档内容进行解析,并在内存中建立一个倒排索引,相当于对目前处理的文档子集单独在内存中建立起了一整套倒排索引,和最终索引相比,其结构和形式是相同的,区别只是这个索引只是部分文档的索引而非全部文档的索引。

② 当内存占满后,为了腾出内存空间,将整个内存中建立的倒排索引写入磁盘临时文件中,然后彻底清除所占内存,这样就空出内存来进行后续文档的处理。

③ 每一轮处理都会在磁盘产生一个对应的临时文件,当所有文档处理完成后,在磁盘中会有多个临时文件,为了产生最终的索引,需要将这些临时文件合并形成最终索引。

2. 倒排索引的不变性

早期的全文检索会为整个文档集合建立一个很大的倒排索引并将其写入到磁盘。 一旦新的索引就绪,旧的就会被其替换,这样最近的变化便可以被检索到。

倒排索引被写入磁盘后是不可改变的:它永远不会修改。 不变性有重要的价值:

  1. 不需要锁。如果你从来不更新索引,你就不需要担心多进程同时修改数据的问题。
  2. 一旦索引被读入内核的文件系统缓存,便会留在哪里,由于其不变性。只要文件系统缓存中还有足够的空间,那么大部分读请求会直接请求内存,而不会命中磁盘。这提供了很大的性能提升。
  3. 其它缓存(像filter缓存),在索引的生命周期内始终有效。它们不需要在每次数据改变时被重建,因为数据不会变化。
  4. 写入单个大的倒排索引允许数据被压缩,减少磁盘 I/O 和 需要被缓存到内存的索引的使用量。

当然,一个不变的索引也有不好的地方。主要事实是它是不可变的! 你不能修改它。如果你需要让一个新的文档可被搜索,你需要重建整个索引。这要么对一个索引所能包含的数据量造成了很大的限制,要么对索引可被更新的频率造成了很大的限制。

3. 动态更新索引 3.1 动态更新索引原理

倒排索引一旦被写入磁盘就是不可更改的,如果搜索引擎需要处理的文档集合是静态集合,那么在索引建立好之后,就可以一直用建好的索引响应用户查询请求。但是,在真实环境中,搜索引擎需要处理的文档集合往往是动态集合,即在建好初始的索引后,后续不断有新文档进入系统,同时原先的文档集合内有些文档可能被删除或者内容被更改。问题是怎样在保留不变性的前提下实现倒排索引的更新呢?答案是: 用更多的索引,即增加临时索引。

在动态更新索引中,有3个关键的索引结构:倒排索引、临时索引和已删除文档列表。

① 倒排索引就是对初始文档集合建立好的索引结构,该索引存在磁盘中,不可改变。

② 临时索引是在内存中实时建立的倒排索引,该索引存储在内存中,当新增文档进入系统,解析文档,之后更新内存中维护的临时索引,文档中出现的每个单词,在其倒排列表末尾追加倒排列表项,随着新加入系统的文档越来越多,临时索引消耗的内存也会随之增加,一旦临时索引将指定的内存消耗光,要考虑将临时索引的内容更新到磁盘索引中,以释放内存空间来容纳后续的新进文档。

③ 已删除文档列表则用来存储已被删除的文档的相应文档ID,形成一个文档ID列表。这里需要注意的是:当一篇文档内容被更改,可以认为是旧文档先被删除,之后向系统内增加一篇新的文档,通过这种间接方式实现对内容更改的支持。

当系统发现有新文档进入时,立即将其加入临时索引中。有文档被删除时,则将其加入删除文档队列。文档被更改时,则将原先文档放入删除队列,解析更改后的文档内容,并将其加入临时索引中。通过这种方式可以满足实时性的要求。

如果用户输入查询请求,则搜索引擎同时从倒排索引和临时索引中读取用户查询单词的倒排列表,找到包含用户查询的文档集合,并对两个结果进行合并,之后利用删除文档列表进行过滤,将搜索结果中那些已经被删除的文档从结果中过滤,形成最终的搜索结果,并返回给用户。这样就能够实现动态环境下的准实时搜索功能。

注意:在内存中的临时索引是不断变化的,但是这个临时索引一旦写进磁盘,就是不可变的。

3.2 新增文档

通过增加临时索引来反映新近的修改,而不是直接重写整个倒排索引,来保证在保留倒排索引不变性的前提下实现倒排索引的更新,下面我们一起看看整个实现过程。

Elasticsearch 基于 Lucene, 这个java库引入了按段搜索的概念。 每一段本身都是一个倒排索引, 但索引在 Lucene 中除表示所有段的集合外, 还增加了提交点的概念,提交点是一个列出了所有已知段的文件。

我想你可能有个疑问,到底什么是段?为什么引入段这个概念?

在早期全文检索中为整个文档集合建立了一个很大的倒排索引,并将其写入磁盘中,如果需要让一个新的文档可被搜索,你需要重建整个索引。这种方式在数据量很大时效率很低,并且由于创建一次索引的成本很高,所以对数据的更新不能过于频繁,也就不能保证时效性,因此提出了段的概念,即将一个索引文件拆分为多个文件,每个子文件叫做段,每个段都是一个被写入磁盘的倒排索引,因此段具有不变性。

这样听起来还是有点抽象,我们来看图理解吧。

① 当新增一个文档时,这个文档会被添加到内存索引缓存中,随后解析文档,更新内存中维护的临时索引,文档中出现的每个单词,在其倒排列表末尾追加倒排列表项。

② 随着内存索引缓存中的的文档越来越多,临时索引消耗的内存也会随之增加,一旦临时索引将指定的内存消耗光,要考虑将临时索引的内容更新到磁盘索引中,以释放内存空间来容纳后续的新进文档。

我们知道:一个ES索引包含多个分片,一个分片对应的就是一个Lucene索引,一个Lucene索引又由多个段组成,每个段都是一个被写入磁盘的倒排索引,Luncene中还有个文件,用来记录所有段的信息,叫做提交点。


当缓存被提交时:

  1. 一个新的段被写入磁盘。(这个段就是缓存中维护的临时倒排索引)

  2. 一个新的包含新段名字的提交点被写入磁盘。

  3. 磁盘进行同步。(所有在文件系统缓存中等待的写入都刷新到磁盘,以确保它们被写入物理文件)

  4. 新的段被开启,让它包含的文档可见以被搜索。

  5. 内存缓存被清空,等待接收新的文档。

每一个倒排索引都会被轮流查询到(从最早的开始)查询完后再对结果进行合并。当一个查询被触发,所有已知的段按顺序被查询。词项统计会对所有段的结果进行聚合,以保证每个词和每个文档的关联都被准确计算。 这种方式可以用相对较低的成本将新文档添加到索引。

3.3 删除和更新文档

段是不可改变的,所以既不能从把文档从旧的段中移除,也不能修改旧的段来进行反映文档的更新。 取而代之的是,每个提交点会包含一个 .del 文件,文件中会列出这些被删除文档的段信息。

当一个文档被 “删除” 时,它实际上只是在 .del 文件中被 标记 删除。一个被标记删除的文档仍然可以被查询匹配到, 但它会在最终结果被返回前从结果集中移除。

文档更新也是类似的 *** 作方式:当一个文档被更新时,旧版本文档被标记删除,文档的新版本被索引到一个新的段中。 可能两个版本的文档都会被一个查询匹配到,但被删除的那个旧版本文档在结果集返回前就已经被移除。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5636218.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存