Elasticsearch Document写入、删除、查询以及搜索原理

Elasticsearch Document写入、删除、查询以及搜索原理

序号内容链接地址
1SpringBoot整合Elasticsearch7.6.1https://blog.csdn.net/miaomiao19971215/article/details/105106783
2Elasticsearch Filter执行原理https://blog.csdn.net/miaomiao19971215/article/details/105487446
3Elasticsearch 倒排索引与重建索引https://blog.csdn.net/miaomiao19971215/article/details/105487532
4Elasticsearch Document写入原理https://blog.csdn.net/miaomiao19971215/article/details/105487574
5Elasticsearch 相关度评分算法https://blog.csdn.net/miaomiao19971215/article/details/105487656
6Elasticsearch Doc valueshttps://blog.csdn.net/miaomiao19971215/article/details/105487676
7Elasticsearch 搜索技术深入https://blog.csdn.net/miaomiao19971215/article/details/105487711
8Elasticsearch 聚合搜索技术深入https://blog.csdn.net/miaomiao19971215/article/details/105487885
9Elasticsearch 内存使用https://blog.csdn.net/miaomiao19971215/article/details/105605379
10Elasticsearch ES-Document数据建模详解https://blog.csdn.net/miaomiao19971215/article/details/105720737

一. 写入原理

ES为了实现进实时搜索,在写入Document时利用了Buffer(内存),OS Cache(系统缓存,属于系统内存的一部分),Disk(磁盘)三种存储方式,尽可能的提升搜索的能力。ES的底层lucene实现的,在 luncene中一个index会被分为若干个数据段segment,每一个segment都会存放index的部分document。从流程上讲,ES会先把一个index中的document分散存储在若干个shard(指的是主分片)上,在shard中,使用若干个segment来存储具体的数据。
ES写入数据的流程大致如下:
在这里插入图片描述

  1. 客户端发起请求(增、删、改)到ES中。
  2. ES将本次请求要操作的document写入到buffer中。ES为了保证搜索的近实时(Near Real Time 简称 NRT),默认每秒刷新一次buffer,这个刷新时间间隔可以手动修改,也可以通过命令触发buffer的刷新。建议刷新时间间隔设置在1秒左右,好处使在服务器宕机后,只会丢失1秒左右的数据。当然了,如果Buffer中没有任何数据,则不会执行refresh操作(总不能创建空文件吧)
POST /index_name/_refresh

PUT index_name
{
  "settings": {
    "number_of_shards": 5,
    "number_of_replicas": 1,
    "refresh_interval": "1s"
  }
}
  1. ES在将document写入到缓存的同时,也会将本次操作的具体内容写入到translog文件中,这份文件存在的意义在于即便ES宕机了,也能尽可能的减少丢失的数据(简单来说,就是把translog中的记录重新执行一遍),当然translog也不能保证数据绝对不丢失,其原因在第6点详细的讲出了。由于translog存储在磁盘Disk中,因此为了提高访问效率,ES与translog文件之间会建立并保持一个长连接(不然每次访问都要获取和释放文件流)。 此时,写入的数据不能对外提供搜索
  2. 步骤2中提到过ES每隔一段时间就会刷新buffer,这个刷新的动作会在内存中创建一个全新的index segment,并将buffer中的document数据全部到这个新的index segment中。值得注意的是,index segment同样是文件,只不过我们目前访问的是内存中的File(ES底层使用java开发,new File()后文件会被读取到内存当中)。
    segment中存储的是buffer指定时间间隔内接收到的document写操作数据(因为Disk与内存有速度差,为了让数据持久化落盘的速度适应数据写入内存的速度,我们使用了buffer,index segment,比如数据写入了10秒,默认每秒刷新一次buffer,则产生10个index segment,而来得及写入Disk的index segment可能只有2个)。此外 ,index segment已经是新增数据被处理成倒排索引后的数据结果了。
  3. 在index segment被创建并写入了来自buffer的数据后,ES会立刻将index segment对应的File写入到系统缓存OS Cache中并打开(也就是说,数据写入磁盘文件之前,会先写入OS Cache缓存一下),这样就可以立刻为客户端提供最新数据的请求服务,而不必等待index semeng写入到磁盘后,再打开index segment。毕竟IO操作是一个重量级的操作,非常费时,一定会影响ES的近实时搜索能力。此时,写入的数据可以对外提供搜索。 这里再次体现了ES的NRT特性,写入到ES的数据,1秒后才能对外提供搜索服务。
  4. 前面说了,translog中记录的是ES操作的过程,万一遇到系统宕机,在系统重启后,ES会重新读取磁盘Disk中保存的数据(一份份的 index segment文件)至系统缓存,接着读取translog中的操作日志,并逐条执行,以此来达到恢复数据的目的。ES默认每隔5秒执行一次translog文件的持久化。translog最初只是写到了OS Cache,只有在持久化时,才会真正强制刷新到本次磁盘上。如果在持久化的过程中恰好ES在做document写操作,那么本次持久化操作将暂停,直到写操作彻底完成后,才继续执行。translog的持久化方式默认为同步(先写数据到Buffer,写完了再写translog日志到OS Cache),如果修改成异步(边写入数据,边持久化日志),那么在translog持久化的过程中新执行的写操作对应的日志就会丢失,假如恰好此时ES所在的服务器宕机,那么这段时间还没来得及持久化到Disk,仅位于内存中index segment或OS Cache缓存中的数据便无法恢复,永久丢失。还有一种情况,translog日志目前仅停留在OS Cache,此时系统宕机,那么translog仍然会永久丢失。这里体现了ES可能会丢失数据(5秒内translog中的数据),如果真的不想丢失任何数据,我们可以将index.translog.durability的值设置成"request",也就是说,每次请求后立刻写入并同步translog至磁盘。当然,这样做的代价是至少让写入的效率下降一个数量级。
PUT index_name
{
  "settings": {
    "number_of_shards": 5,
    "number_of_replicas": 1,
    "index.translog.durability" : "async",
    "index.translog.sync_interval" : "5s"
  }
}
  1. 随着时间的推移,translog文件会不断的增大,在内存中积压的数量众多的index segment file的文件流也在不断的增大,OS Cache中积压的数据也越来越大,当translog文件大到一定程度或默认30分钟执行一次,ES会自动触发commit操作(又叫flush操作)。commit操作的具体内容有:
    1. 将buffer中的数据刷新到一个新的index segment中;
    2. 将index segment写入到OS Cache并打开index segment为搜索提供服务;
    3. 执行一个commit point操作,将OS Cache中所有的index segment标识记录在这个commit point中,并持久化到系统磁盘Disk;
    4. commit point操作会触发fsync操作(file sync),将内存中已经写入数据的index segment落盘(强制刷新)到Disk磁盘上,持久化成文件。
    5. 清空本次持久化的index segment对应在translog中的日志。
  2. 按照上述的流程来看,每1秒会生成一个index segment文件,每30分钟会将index segment文件流持久化到磁盘,照这样来看,磁盘中的index segment文件会非常多,从而需要处于开启状态的index segment也非常多,在执行搜索操作时,找到数据对应的index segment就会比较费时了。但不用担心,因为ES会定期进行Merge操作,Merge在接下来的删除原理中详细的说明了。

二. 删除原理

ES为了保证数据的近实时搜索能力,不会直接在物理磁盘中删除目标数据所在的segment文件,而是先把待删除的数据放入一个.del文件中,在执行segment merge操作时,通过参考.del文件,忽略掉已被删除的数据,最终把大量的segment file合并成一个或几个segment file。

merge的大致流程如下:
1. ES会选取一些大小相近的segment文件流,合并成一个大的segment文件流(注意: segment可能是尚未持久化到磁盘的segment file,也可能是已经持久化到磁盘的segment file,不管是哪种状态的segment file,此时它们都在OS Cache中)。
2. 执行commit操作,在Disk中记录commit point,这个commit point不仅包含新增的segment,还包含合并后,需要被删除的segment源文件的标识,刚才曾提到过,有些segment file中的数据会被删除(合并时被忽略),如果一个segment file内的数据都被删除(被合并或忽略了),则这些segment就会标记"被删除"。
3. commit操作结束后,ES会将merge后的segment文件重新打开,为搜索提供服务,而那些旧的需要被删除的segment文件则进行关闭并物理删除。

此外,ES在执行search搜索时,目标数据可能位于不同的index segment上,因此ES会扫描所有已经开打的index segment文件并找到目标数据。文件打开指的是将数据读到了OS Cache系统缓存。

在buffer数据写入到segment的同时,会生成一个.del文件专门记录哪一个index segment中哪一条document是deleted状态(在merge后,这个.del文件会被更新)。因此ES搜索时,如果在多个index segment中查到了不同版本(version)的相同id值的document时,会根据.del文件中的记录来继续过滤,保证搜索结果的唯一性和正确性(比如segment1中包含一条document version=1,对应新增状态;而segment2中包含相同id的document version=2,对应更新状态。由于后者的版本号更新,因此在.del中,version1被视作旧document,会被标记成deleted状态,从而在搜索时就会得到segment2中包含的version=2的数据了)。

三. 查询原理

  1. 假设客户端请求查询_id=10的数据。
  2. 请求发送到ES集群中的任意节点,此时该节点成为本次请求的协调节点。协调节点默认根据数据的_id作为routing(可以手动指定,只需要在查询时增加_routing参数即可)进行hash算法,Hash(routing) % number_of_shards,假设计算出目标数据存放的shard的下标是3。接着,协调节点请求master节点,获取下标为3的shard所在节点的访问路径、端口等信息,并将查询请求转发至目标节点中。(注意:显然下标为3的shard不一定只有一个,有可能存在一个primary shard和多个replica shard的场景,至于到底把请求发送到哪一个shard上,取决于随机轮寻算法round-robin)
  3. 目标节点在目标分片内根据_id轻松的查询到数据,并将数据回传给协调节点。
  4. 协调节点将数据返回给客户端。

四. 搜索原理

  1. 假设客户端请求查询某一个field的值为"hello java"。
  2. 请求发送到ES集群中的任意节点,此时该节点成为本次请求的协调节点。此时,协调节点不知道目标数据到底存放在哪个节点的那个分片上,因此协调节点会把请求转发到ES集群当中的每一个节点中。
  3. 其它节点接收到请求后,会在自己节点的分片中(Q: 此处不确定是否是当前节点的所有分片)搜索目标结果的_id,并返回给协调节点。(当然了,协调节点自身也会搜索自己拥有的分片)
  4. 协调节点收集、整理各个节点返回的目标数据的_id,再通过查询原理那套流程从目标shard中获取document,最后将document整合并返回给客户端。

五. 注意

buffer和尚未写入系统缓存的index segment(就是一段倒排索引)存储在堆内存,受jvm参数控制。

系统缓存 OScache在这里可以被看做是"文件系统缓存",用于缓存打开后的segment file(段文件),存储在非堆内存,受操作系统控制。

非堆内存越大,能够打开并缓存的segment file(段文件)就越多,搜索和聚合时,能够直接从内存中获取的热数据也就越多(不需要通过IO,在磁盘中找到尚未打开的segment file,读取文件内容)。

搜索数据时,首先在OS Cache中进行搜索,如果找不到数据,则在磁盘中找到对应的index segment文件并打开,读取数据至堆内存中,接着,在堆内存中对数据进行聚合、排序等操作,最后把数据返回给协调节点,最终交给调用方。此外,新读取到堆内存的segment file会被Lucense缓存至非堆内存中。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值