elasticsearch笔记整理(十一)-基础入门-分片内部原理(倒排索引、实时搜索、持久化变更和段合并)-选摘自《elasticsearch权威指南》

作者: admin 分类: ELK 发布时间: 2019-06-01 22:30  阅读: 65 views

elasticsearch中究竟什么是一个分片,它是如何工作的?
● 为什么搜索是近实时的?
● 为什么文档的 CRUD (创建-读取-更新-删除) 操作是 实时 的?
● Elasticsearch 是怎样保证更新被持久化在断电时也不丢失数据?
● 为什么删除文档不会立刻释放空间?
● refresh, flush, 和 optimize API 都做了什么, 你什么情况下应该使用他们?

 

1.使文本可以被搜索

 

必须解决的第一个挑战是如何使文本可被搜索。 传统的数据库每个字段存储单个值,但这对全文检索并不够。文本字段中的每个单词需要被搜索,对数据库意味着需要单个字段有索引多值(这里指单词)的能力。

最好的支持 一个字段多个值 需求的数据结构是倒排索引。 倒排索引包含一个有序列表,列表包含所有文档出现过的不重复个体,或称为词项,对于每一个词项,包含了它所有曾出现过文档的列表。

 

Term  | Doc 1 | Doc 2 | Doc 3 | ...
------------------------------------
brown |   X   |       |  X    | ...
fox   |   X   |   X   |  X    | ...
quick |   X   |   X   |       | ...
the   |   X   |       |  X    | ...

这个倒排索引相比特定词项出现过的文档列表,会包含更多其它信息。它会保存每一个词项出现过的文档总数, 在对应的文档中一个具体词项出现的总次数,词项在文档中的顺序,每个文档的长度,所有文档的平均长度,等等。这些统计信息允许 Elasticsearch 决定哪些词比其它词更重要,哪些文档比其它文档更重要。

为了能够实现预期功能,倒排索引需要知道集合中的 所有 文档,这是需要认识到的关键问题。

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

 

 

2.不变性

 

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

 

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

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

 

3.动态更新索引

 

下一个需要被解决的问题是怎样在保留不变性的前提下实现倒排索引的更新? 答案是: 用更多的索引。

通过增加新的补充索引来反映新近的修改,而不是直接重写整个倒排索引。每一个倒排索引都会被轮流查询到–从最早的开始–查询完后再对结果进行合并。

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



 

逐段搜索会以如下流程进行工作:

 

  1. 新文档被收集到内存索引缓存, 
  2. 不时地, 缓存被 提交 :
      ○ 一个新的段--一个追加的倒排索引--被写入磁盘。
      ○ 一个新的包含新段名字的 提交点 被写入磁盘。
      ○ 磁盘进行 同步 — 所有在文件系统缓存中等待的写入都刷新到磁盘,以确保它们被写入物理文件。
  3. 新的段被开启,让它包含的文档可见以被搜索。
  4. 内存缓存被清空,等待接收新的文档。

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

 

4.删除和更新

 

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

 

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

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

 

5.近实时搜索

 

随着按段(per-segment)搜索的发展, 一个新的文档从索引到可被搜索的延迟显著降低了。新文档在几分钟之内即可被检索,但这样还是不够快。

磁盘在这里成为了瓶颈。 提交(Commiting)一个新的段到磁盘需要一个 fsync 来确保段被物理性地写入磁盘,这样在断电的时候就不会丢失数据。 但是 fsync 操作代价很大; 如果每次索引一个文档都去执行一次的话会造成很大的性能问题。

我们需要的是一个更轻量的方式来使一个文档可被搜索,这意味着 fsync 要从整个过程中被移除。

在Elasticsearch和磁盘之间是文件系统缓存。 像之前描述的一样, 在内存索引缓冲区中的文档会被写入到一个新的段中。 但是这里新段会被先写入到文件系统缓存–这一步代价会比较低,稍后再被刷新到磁盘–这一步代价比较高。不过只要文件已经在缓存中, 就可以像其它文件一样被打开和读取了。


 

6,持久化变更

 

如果没有用 fsync 把数据从文件系统缓存刷(flush)到硬盘,我们不能保证数据在断电甚至是程序正常退出之后依然存在。为了保证 Elasticsearch 的可靠性,需要确保数据变化被持久化到磁盘。

在 动态更新索引,我们说一次完整的提交会将段刷到磁盘,并写入一个包含所有段列表的提交点。Elasticsearch 在启动或重新打开一个索引的过程中使用这个提交点来判断哪些段隶属于当前分片。

即使通过每秒刷新(refresh)实现了近实时搜索,我们仍然需要经常进行完整提交来确保能从失败中恢复。但在两次提交之间发生变化的文档怎么办?我们也不希望丢失掉这些数据。

Elasticsearch 增加了一个 translog ,或者叫事务日志,在每一次对 Elasticsearch 进行操作时均进行了日志记录。通过 translog ,整个流程看起来是下面这样:

 

1.一个文档被索引之后,就会被添加到内存缓冲区,并且 追加到了 translog
2.刷新(refresh)使分片,分片每秒被刷新(refresh)一次:
  ● 这些在内存缓冲区的文档被写入到一个新的段中,且没有进行 fsync 操作。
  ● 这个段被打开,使其可被搜索。
  ● 内存缓冲区被清空。
3.这个进程继续工作,更多的文档被添加到内存缓冲区和追加到事务日志
4.每隔一段时间--例如 translog 变得越来越大--索引被刷新(flush);一个新的 translog 被创建,并且一个全量提交被执行
  ● 所有在内存缓冲区的文档都被写入一个新的段。
  ● 缓冲区被清空。
  ● 一个提交点被写入硬盘。
  ● 文件系统缓存通过 fsync 被刷新(flush)。
  ● 老的 translog 被删除。

translog 提供所有还没有被刷到磁盘的操作的一个持久化纪录。当 Elasticsearch 启动的时候, 它会从磁盘中使用最后一个提交点去恢复已知的段,并且会重放 translog 中所有在最后一次提交后发生的变更操作。

translog 也被用来提供实时 CRUD 。当你试着通过ID查询、更新、删除一个文档,它会在尝试从相应的段中检索之前, 首先检查 translog 任何最近的变更。这意味着它总是能够实时地获取到文档的最新版本。

 

官方文档说明:https://www.elastic.co/guide/cn/elasticsearch/guide/current/translog.html

 

7.段合并

 

由于自动刷新流程每秒会创建一个新的段 ,这样会导致短时间内的段数量暴增。而段数目太多会带来较大的麻烦。 每一个段都会消耗文件句柄、内存和cpu运行周期。更重要的是,每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢。

Elasticsearch通过在后台进行段合并来解决这个问题。小的段被合并到大的段,然后这些大的段再被合并到更大的段。

段合并的时候会将那些旧的已删除文档 从文件系统中清除。 被删除的文档(或被更新文档的旧版本)不会被拷贝到新的大段中。
启动段合并不需要你做任何事。进行索引和搜索时会自动进行。

 

1、 当索引的时候,刷新(refresh)操作会创建新的段并将段打开以供搜索使用。
2、 合并进程选择一小部分大小相似的段,并且在后台将它们合并到更大的段中。这并不会中断索引和搜索。
3、 合并完成时的活动:
  ● 新的段被刷新(flush)到了磁盘。   ** 写入一个包含新段且排除旧的和较小的段的新提交点。
  ● 新的段被打开用来搜索。
  ● 老的段被删除。


合并大的段需要消耗大量的I/O和CPU资源,如果任其发展会影响搜索性能。Elasticsearch在默认情况下会对合并流程进行资源限制,所以搜索仍然有足够的资源很好地执行。

https://www.elastic.co/guide/cn/elasticsearch/guide/current/merge-process.html


   原创文章,转载请标明本文链接: elasticsearch笔记整理(十一)-基础入门-分片内部原理(倒排索引、实时搜索、持久化变更和段合并)-选摘自《elasticsearch权威指南》

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

更多阅读