ElasticSearch【有与无】【搜索引擎】【ES11】分片【选读】

本文详细探讨了Elasticsearch的工作原理,包括其如何利用倒排索引使文本可搜索、动态索引机制、近实时搜索特性、持久化变更以及合并段以优化性能。通过理解这些核心概念,读者将更好地掌握Elasticsearch的高效使用方法。

目录

1.简介

1.1.使文本可以被搜索

不可变性

1.2.动态索引

删除和更新

1.3.近实时搜索

refeash API

1.4.持久化变更

flush API

1.5.合并段

optimize API


1.简介

分片到底是什么,它怎样工作?

  • 为什么搜索是近实时的?
  • 为什么文档的CRUD操作是实时的?
  • ES怎样保证更新持久化,即使断电也不会丢失?
  • 为什么删除文档不会立即释放空间?
  • 什么是 refresh,flush, optimize API  ,以及什么时候你该使用它们?

 

1.1.使文本可以被搜索

让文本中的每个单词都可以被搜索,数据库需要存多个值。

支持一个字段多个值的最佳数据结构是倒排索引。倒排索引包含了出现在所有文档中唯一的值或词的有序列表,以及每个词所属的文档列表。

注意
当讨论倒排索引时,是指把文档加入索引。因为之前,一个倒排索引是用来索引整个非结构化的文本文档。
ES中的文档是一个结构化的JSON文档。实际上,每一个JSON文档中被索引的字段都有它自己的倒排索引

为了实现倒排索引预期的功能,它必须要知道集合中所有的文档。

在全文检索的早些时候,会为整个文档集合建立一个大索引,并且写入磁盘。只有新的索引准备好了,它就会替代旧的索引,最近的修改才可以被检索。

 

不可变性

写入磁盘的倒排索引是不可变的,它有如下好处:

  • 不需要锁。如果从来不需要更新一个索引,就不必担心多个程序同时尝试修改。
  • 一旦索引被读入文件系统的缓存,它就不会改变。只要文件系统缓存有足够的空间,大部分的读会直接访问内存而不是磁盘。有助于性能提升。
  • 在索引的声明周期内,所有的其他缓存都可用。它们不需要在每次数据变化了都重建,因为数据不会变。
  • 写入单个大的倒排索引,可以压缩数据,较少磁盘IO和需要缓存索引的内存大小。

【缺点】

如果想要搜索一个新文档,必须重见整个索引。

这不仅严重限制了一个索引所能装下的数据,还有一个索引可以被更新的频次。

 

1.2.动态索引

如何在保持不可变好处的同时更新倒排索引?

答:使用多个索引

不是重写整个倒排索引,而是增加额外的索引反映最近的变化。每个倒排索引都可以按顺序查询,从最老的开始,最后把结果聚合。

 

Elasticsearch底层依赖的Lucene,引入了 per-segment search  的概念。

一个段(segment)是有完整功能的倒排索引,但Lucene中的索引指的是段的集合,再加上提交点(commit point,包括所有段的文件)

新的文档,在被写入磁盘的段之前,首先写入内存区的索引缓存

 

 

索引vs分片
  Lucene索引是Elasticsearch中的分片,Elasticsearch中的索引是分片的集合。
  当Elasticsearch搜索索引时,发送查询请求给该索引下的所有分片,然后过滤这些结果,聚合成全局的结果。

【 per-segment search 工作】

    1. 新的文档首先写入内存区的索引缓存。

    2. 这些buffer被提交: 

           一个新的段——额外的倒排索引——写入磁盘。

           新的提交点写入磁盘,包括新段的名称。

           磁盘是fsync’ed(文件同步)——所有写操作等待文件系统缓存同步到磁盘,确保它们可以被物理写入。

    3. 新段被打开,它包含的文档可以被检索

    4. 内存的缓存被清除,等待接受新的文档

当一个请求被接受,所有段依次查询。所有段上的Term统计信息被聚合,确保每个term和文档的相关性被正确计算。通过这种方式,新的文档以较小的代价加入索引。

 

删除和更新

段是不可变的,所以文档既不能从旧的段中移除,旧的段也不能更新以反映文档最新的版本。

相反,每一个提交点包括一个.del文件,包含了段上已经被删除的文档。

当一个文档被删除,它实际上只是在.del文件中被标记为删除,依然可以匹配查询,但是最终返回之前会被从结果中删除。

文档的更新操作是类似的:当一个文档被更新,旧版本的文档被标记为删除,新版本的文档在新的段中索引。也许该文档的不同版本都会匹配一个查询,但是更老版本会从结果中删除。

 

1.3.近实时搜索

因为 per-segment search 机制,索引和搜索一个文档之间是有延迟的。

新的文档会在几分钟内可以搜索,但是这依然不够快。

 

磁盘是瓶颈。

提交一个新的段到磁盘需要 fsync 操作,确保段被物理地写入磁盘,即时电源失效也不会丢失数据。

但是 fsync 是昂贵的,不能在每个文档被索引的时就触发。

所以需要一种更轻量级的方式使新的文档可以被搜索,这意味这移除 fsync  。

位于Elasticsearch和磁盘间的是文件系统缓存。

Lucene允许新段写入打开,好让它们包括的文档可搜索,而不用执行一次全量提交。这是比提交更轻量的过程,可以经常操作,而不会影响性能。

 

refeash API

在Elesticsearch中,这种写入打开一个新段的轻量级过程,叫做refresh。

默认情况下,每个分片每秒自动刷新一次。

这就是为什么说Elasticsearch是近实时的搜索了:文档的改动不会立即被搜索,但是会在一秒内可见。

【手动刷新】

POST /_refresh      // 刷新所有索引
POST /blogs/_refresh   // 只refresh 索引 blogs

虽然刷新比提交更轻量,但是它依然有消耗。
人工刷新在测试写的时有用,但是不要在生产环境中每写一次就执行刷新,这会影响性能。
相反,你的应用需要意识到ES近实时搜索的本质,并且容忍它。

不是所有的用户都需要每秒刷新一次。也许你使用ES索引百万日志文件,你更想要优化索引的速度,而不是进实时搜索。你可以通过修改配置项 refresh_interval  减少刷新的频率:

PUT /my_logs
{
    "settings": {
        "refresh_interval": "30s"  // 每30s refresh一次 my_logs;在存在的索引上动态更新
    }
}

【关闭自动刷新】

PUT /my_logs/_settings
{ "refresh_interval": -1 }

 

1.4.持久化变更

没用 fsync 同步文件系统缓存到磁盘,不能确保电源失效,甚至正常退出应用后,数据的安全。为了ES的可靠性,需要确保变更持久化到磁盘。
一次全提交同步段到磁盘,写提交点,这会列出所有的已知的段。在重启,或重新打开索引时,ES使用这次提交点决定哪些段属于当前的分片。

当通过每秒的刷新获得近实时的搜索,依然需要定时地执行全提交确保能从失败中恢复。但是提交之间的文档怎么办?也不想丢失它们。

ES增加了事务日志( translog  ),来记录每次操作。有了事务日志,过程现在如下:

1. 当一个文档被索引,它被加入到内存缓存,同时加到事务日志。

2. refresh使得分片的进入如下图描述的状态。每秒分片都进行refeash:

  • 内存缓冲区的文档写入到段中,但没有fsync。
  • 段被打开,使得新的文档可以搜索。
  • 缓存被清除

3. 随着更多的文档加入到缓存区,写入日志,这个过程会继续

4. 不时地,比如日志很大了,新的日志会创建,会进行一次全提交:

  • 内存缓存区的所有文档会写入到新段中。
  • 清除缓存
  • 一个提交点写入硬盘
  • 文件系统缓存通过fsync操作flush到硬盘
  • 事务日志被清除

事务日志记录了没有flush到硬盘的所有操作。当故障重启后,ES会用最近一次提交点从硬盘恢复所有已知的段,并且从日志里恢复所有的操作。
事务日志还用来提供实时的CRUD操作。当尝试用ID进行CRUD时,它在检索相关段内的文档前会首先检查日志最新的改动。这意味着ES可以实时地获取文档的最新版本。

 

flush API

在ES中,进行一次提交并删除事务日志的操作叫做 flush  。

分片每30分钟,或事务日志过大会进行一次flush操作。

【手动flush】(很少)

POST /blogs/_flush  // flush索引 blogs

POST /_flush?wait_for_ongoing  // flush所有索引,等待操作结束再返回

当要重启或关闭一个索引,flush该索引是很有用的。

当ES尝试恢复或者重新打开一个索引时,它必须重放所有事务日志中的操作,所以日志越小,恢复速度越快。

 

1.5.合并段

【问题】

通过每秒自动刷新创建新的段,用不了多久段的数量就爆炸了。有太多的段是一个问题。每个段消费文件句柄,内存,cpu资源。更重要的是,每次搜索请求都需要依次检查每个段。段越多,查询越慢。

【解决】

小段被合并成大段,再合并成更大的段。

这是旧的文档从文件系统删除的时候。旧的段不会再复制到更大的新段中。

这个过程你不必做什么。当你在索引和搜索时ES会自动处理。

这个过程如图:两个提交的段和一个未提交的段合并为了一个更大的段所示:

1. 索引过程中,refresh会创建新的段,并打开它。

2. 合并过程会在后台选择一些小的段合并成大的段,这个过程不会中断索引和搜索。

3. 下图描述了合并后的操作:

  • 新的段flush到了硬盘。
  • 新的提交点写入新的段,排除旧的段。
  • 新的段打开供搜索。
  • 旧的段被删除。

合并大的段会消耗很多IO和CPU,如果不检查会影响到搜素性能。默认情况下,ES会限制合并过程,这样搜索就可以有足够的资源进行。

 

optimize API

optimize API 最好描述为强制合并段API。

它强制分片合并段以达到指定 max_num_segments 参数。这是为了减少段的数量(通常为1)达到提高搜索性能的目的。

警告
不要在动态的索引(正在活跃更新)上使用 optimize API。后台的合并处理已经做的很好了,优化命令会阻碍它的工作。不要干涉!

在特定的环境下,optimize API 是有用的。

典型的场景是记录日志,这种情况下日志是按照每天,周,月存入索引。旧的索引一般是只可读的,是不可能修改的。

这种情况下,把每个索引的段降至1是有效的。搜索过程就会用到更少的资源,性能更好

【举例】

POST /logstash-2014-10/_optimize?max_num_segments=1  // 把索引中的每个分片都合并成一个段

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琴 韵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值