【ES】Elasticsearch-深入理解索引原理

news2025/1/11 19:46:46

文章目录

  • Elasticsearch-深入理解索引原理
    • 读操作
    • 更新操作
  • SHARD
    • 不变性
    • 动态更新索引
    • 删除和更新
    • 实时索引
    • 更新持久化
    • Segment合并
  • 近实时搜索,段数据刷新,数据可见性更新和事务日志
    • 更新索引并且将改动提交
    • 修改Searcher对象默认的更新时间

Elasticsearch-深入理解索引原理

索引(Index)

ES将数据存储于一个或多个索引中,索引是具有类似特性的文档的集合。类比传统的关系型数据库领域来说,索引相当于SQL中的一个数据库,或者一个数据存储方案(schema)。索引由其名称(必须为全小写字符)进行标识,并通过引用此名称完成文档的创建、搜索、更新及删除操作。一个ES集群中可以按需创建任意数目的索引。

我们了解索引的写操作后可知,更新、索引、删除文档都是写操作,这些操作必须在primary shard完全成功后才能拷贝至其对应的replicas上,默认情况下主分片等待所有备份完成索引后才返回客户端。

在这里插入图片描述

步骤:

  1. 客户端向Node1 发送索引文档请求
  2. Node1 根据文档ID(_id字段)计算出该文档应该属于shard0,然后请求路由到Node3的P0分片上
  3. Node3在P0上执行了请求。如果请求成功,则将请求并行的路由至Node1,Node2的R0上。当所有的Replicas报告成功后,Node3向请求的Node(Node1)发送成功报告,Node1再报告至Client。

当客户端收到执行成功后,操作已经在Primary shard和所有的replica shards上执行成功了

在这里插入图片描述

读操作

一个文档可以在primary shard和所有的replica shard上读取。见Figure10

读操作步骤:

1.客户端发送Get请求到NODE1。
2.NODE1使用文档的_id决定文档属于shard 0.shard 0的所有拷贝存在于所有3个节点上。这次,它将请求路由至NODE2。
3.NODE2将文档返回给NODE1,NODE1将文档返回给客户端。 对于读请求,请求节点(NODE1)将在每次请求到来时都选择一个不同的replica。
shard来达到负载均衡。使用轮询策略轮询所有的replica shards。

更新操作

更新操作,结合了以上的两个操作:读、写。见Figure11
在这里插入图片描述

步骤:

1.客户端发送更新操作请求至NODE1
2.NODE1将请求路由至NODE3,Primary shard所在的位置
3.NODE3从P0读取文档,改变source字段的JSON内容,然后试图重新对修改后的数据在P0做索引。如果此时这个文档已经被其他的进程修改了,那么它将重新执行3步骤,这个过程如果超过了retryon_conflict设置的次数,就放弃。
4.如果NODE3成功更新了文档,它将并行的将新版本的文档同步到NODE1和NODE2的replica shards重新建立索引。一旦所有的replica
shards报告成功,NODE3向被请求的节点(NODE1)返回成功,然后NODE1向客户端返回成功。

SHARD

  • 为什么搜索是实时的
  • 为什么文档的CRUD操作是实时的
  • ES怎么保障你的更新在宕机的时候不会丢失
  • 为什么删除文档不会立即释放空间

不变性

写到磁盘的倒序索引是不变的:自从写到磁盘就再也不变。 这会有很多好处:

不需要添加锁。如果你从来不用更新索引,那么你就不用担心多个进程在同一时间改变索引。
一旦索引被内核的文件系统做了Cache,它就会待在那因为它不会改变。只要内核有足够的缓冲空间,绝大多数的读操作会直接从内存而不需要经过磁盘。这大大提升了性能。
其他的缓存(例如fiter cache)在索引的生命周期内保持有效,它们不需要每次数据修改时重构,因为数据不变。
写一个单一的大的倒序索引可以让数据压缩,减少了磁盘I/O的消耗以及缓存索引所需的RAM。

当然,索引的不变性也有缺点。如果你想让新修改过的文档可以被搜索到,你必须重新构建整个索引。这在一个index可以容纳的数据量和一个索引可以更新的频率上都是一个限制。

动态更新索引

如何在不丢失不变形的好处下让倒序索引可以更改?答案是:使用不只一个的索引。 新添额外的索引来反映新的更改来替代重写所有倒序索引的方案。 Lucene引进了per-segment搜索的概念。一个segment是一个完整的倒序索引的子集,所以现在index在Lucene中的含义就是一个segments的集合,每个segment都包含一些提交点(commit point)。见Figure16。新的文档建立时首先在内存建立索引buffer,见Figure17。然后再被写入到磁盘的segment,见Figure18。

在这里插入图片描述

在这里插入图片描述

一个per-segment的工作流程如下:

1.新的文档在内存中组织,见Figure17。
2.每隔一段时间,buffer将会被提交: 一个新的segment(一个额外的新的倒序索引)将被写到磁盘 一个新的提交点(commit point)被写入磁盘,将包含新的segment的名称。 磁盘fsync,所有在内核文件系统中的数据等待被写入到磁盘,来保障它们被物理写入。
3.新的segment被打开,使它包含的文档可以被索引。
4.内存中的buffer将被清理,准备接收新的文档。

当一个新的请求来时,会遍历所有的segments。词条分析程序会聚合所有的segments来保障每个文档和词条相关性的准确。通过这种方式,新的文档轻量的可以被添加到对应的索引中。

删除和更新

segments是不变的,所以文档不能从旧的segments中删除,也不能在旧的segments中更新来映射一个新的文档版本。取之的是,每一个提交点都会包含一个.del文件,列举了哪一个segmen的哪一个文档已经被删除了。 当一个文档被”删除”了,它仅仅是在.del文件里被标记了一下。被”删除”的文档依旧可以被索引到,但是它将会在最终结果返回时被移除掉。

文档的更新同理:当文档更新时,旧版本的文档将会被标记为删除,新版本的文档在新的segment中建立索引。也许新旧版本的文档都会本检索到,但是旧版本的文档会在最终结果返回时被移除。

实时索引

在上述的per-segment搜索的机制下,新的文档会在分钟级内被索引,但是还不够快。 瓶颈在磁盘。将新的segment提交到磁盘需要fsync来保障物理写入。但是fsync是很耗时的。它不能在每次文档更新时就被调用,否则性能会很低。 现在需要一种轻便的方式能使新的文档可以被索引,这就意味着不能使用fsync来保障。 在ES和物理磁盘之间是内核的文件系统缓存。之前的描述中,Figure19,Figure20,在内存中索引的文档会被写入到一个新的segment。但是现在我们将segment首先写入到内核的文件系统缓存,这个过程很轻量,然后再flush到磁盘,这个过程很耗时。但是一旦一个segment文件在内核的缓存中,它可以被打开被读取。
在这里插入图片描述

在这里插入图片描述

更新持久化

不使用fsync将数据flush到磁盘,我们不能保障在断电后或者进程死掉后数据不丢失。ES是可靠的,它可以保障数据被持久化到磁盘。 在2.6.2中,一个完全的提交会将segments写入到磁盘,并且写一个提交点,列出所有已知的segments。当ES启动或者重新打开一个index时,它会利用这个提交点来决定哪些segments属于当前的shard。 如果在提交点时,文档被修改会怎么样?不希望丢失这些修改:

1.当一个文档被索引时,它会被添加到in-memory buffer,并且添加到Translog日志中,见Figure21.

在这里插入图片描述

2.refresh操作会让shard处于Figure22的状态:每秒中,shard都会被refreshed:

在这里插入图片描述

在in-memory buffer中的文档会被写入到一个新的segment,但没有fsync。
in-memory buffer被清空
3.这个过程将会持续进行:新的文档将被添加到in-memory buffer和translog日志中,见Figure23

在这里插入图片描述

4.一段时间后,当translog变得非常大时,索引将会被flush,新的translog将会建立,一个完全的提交进行完毕。见Figure24

在这里插入图片描述

在in-memory中的所有文档将被写入到新的segment
内核文件系统会被fsync到磁盘。
旧的translog日志被删除
translog日志提供了一个所有还未被flush到磁盘的操作的持久化记录。当ES启动的时候,它会使用最新的commit point从磁盘恢复所有已有的segments,然后将重现所有在translog里面的操作来添加更新,这些更新发生在最新的一次commit的记录之后还未被fsync。

translog日志也可以用来提供实时的CRUD。当你试图通过文档ID来读取、更新、删除一个文档时,它会首先检查translog日志看看有没有最新的更新,然后再从响应的segment中获得文档。这意味着它每次都会对最新版本的文档做操作,并且是实时的。

Segment合并

通过每隔一秒的自动刷新机制会创建一个新的segment,用不了多久就会有很多的segment。segment会消耗系统的文件句柄,内存,CPU时钟。最重要的是,每一次请求都会依次检查所有的segment。segment越多,检索就会越慢。

ES通过在后台merge这些segment的方式解决这个问题。小的segment merge到大的,大的merge到更大的。。。

这个过程也是那些被”删除”的文档真正被清除出文件系统的过程,因为被标记为删除的文档不会被拷贝到大的segment中。

合并过程如Figure25:

在这里插入图片描述

1.当在建立索引过程中,refresh进程会创建新的segments然后打开他们以供索引。
2.merge进程会选择一些小的segments然后merge到一个大的segment中。这个过程不会打断检索和创建索引。
在这里插入图片描述

3.Figure26,一旦merge完成,旧的segments将被删除

  • 新的segment被flush到磁盘
  • 一个新的提交点被写入,包括新的segment,排除旧的小的segments
  • 新的segment打开以供索引
  • 旧的segments被删除

merge大的segments会消耗大量的I/O和CPU,严重影响索引性能。默认,ES会节制merge过程来给留下足够多的系统资源。

近实时搜索,段数据刷新,数据可见性更新和事务日志

理想的搜索解决方案是这样的:新的数据一添加到索引中立马就能搜索到。第一眼看上去,这不正是ElasticSearch的工作方式吗,即使是多服务器环境也是如此。但是真实情况不是这样的(至少现在不是),后面会讲到为什么它是似是而非。首先,我们往新创建的索引中添加一个新的文档,命令如下:

curl -XPOST localhost:9200/test/test/1 -d '{ "title": "test" }'

接下来,我们在替换文档的同时查找该文档。我们用如下的链式命令来实现这一点:

curl -XPOST localhost:9200/test/test/1 -d '{ "title": "test2" }' ; curl
localhost:9200/test/test/_search?pretty

上面命令的结果类似如下:

{"ok":true,"_index":"test","_type":"test","_id":"1","_version":2}
{
    "took" : 1,
    "timed_out" : false,
        "_shards" : {
        "total" : 5,
        "successful" : 5,
        "failed" : 0
    },
    "hits" : {
        "total" : 1,
        "max_score" : 1.0,
        "hits" : [ {
            "_index" : "test",
            "_type" : "test",
            "_id" : "1",
            "_score" : 1.0, "_source" : { "title": "test" }
        } ]
    }
}

第一行是第一个命令,即索引命令的返回结果。可以看到,数据更新成功。因此,第二个命令,即查询命令查询到的文档title域值应该为test2。但是,可以看到结果并不如人所愿。这背后发生了什么呢?

在揭开前一个问题的答案之前,我们先退一步,来了解底层的Apache Lucene工具包是如何让新添加的文档对搜索可见的。

更新索引并且将改动提交

从 第1章 介绍ElasticSearch 的 介绍Apache Lucene一节中,我们已经了解到,在索引过程中,新添加的文档都是写入到段(segments)中。每个段都是有着独立的索引结构,这意味着查询与索引两个过程是可以并行存在的,索引过程中,系统会不定期创建新的段。Apache Lucene通过在索引目录中创建新的segments_N文件来标识新的段。段创建的过程就称为索引的提交。Lucene可以一种安全的方式实现索引的提交——我们可以确定段文件要么全部创建成功,要么失败。如果错误发生,我们可以确保索引状态的一致性。

回到我们的例子中,第一条命令添加文档到索引中,但是没有提交。这就是它的工作方式。然而,索引数据的提交也不能保证数据是搜索可见的。Lucene工具包使用一个名为Searcher的抽象类来读取索引。索引提交操作完成后,Searcher对象需要重新打开才能加载到新创建的索引段。这整个过程称为更新。出于性能的考虑,ElasticSearch会将推迟开销巨大的更新操作,默认情况下,单个文档的添加并不会触发搜索器的更新,Searcher对象会每秒更新一次。这个频率已经比较高了,但是在一些应用程序中,需要更频繁的更新。对面这个需求,我们可以考虑使用其它的解决方案或者再次核实我们是否真的需要这样做。如果确实需要,那么可以使用ElasticSearch API强制更新。比如,上面的例子中,我们可以执行如下的命令强制更新:

curl –XGET localhost:9200/test/_refresh
如果在搜索前执行了上面的命令,那么ElasticSearch就可以搜索到修改后的文档。

修改Searcher对象默认的更新时间

Searcher对象的默认更新时间可以通过使用index.refresh_interval参数来修改,该参数无论是添加到ElasticSearch的配置文件中或者使用update settings API都可以生效。例如:

curl -XPUT localhost:9200/test/_settings -d '{
    "index" : {
        "refresh_interval" : "5m"
    }
}'

上面的命令将使Searcher每5秒钟自动更新一次。请记住在更新两个时间点之间添加到索引的数据对查询是不可见的。

在这里插入图片描述

在这里插入图片描述

总结
  本篇从索引的创建操作和原理等方面介绍了ES索引的一些内容,很多都来自各位大神的总结。经过使用ES越来越多开始作为数据库的辅助。希望大家多多指点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/379840.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS8基础篇9:进程的延迟与周期调度

一、进程的概念 进程:开始执行但是还没有结束的程序的实例 程序:包含可执行代码的文件 进程与程序的关系 进程由程序产生,是一个运行着的、要占系统资源的程序 进程不等于程序 浏览网络时,打开多个IE浏览器程序;…

一文讲清楚如何进行主数据编码

主数据编码作为一类重要的数据资源,在信息化建设中具有重要的地位和作用,是保证现有信息系统和未来新系统建设成功的关键因素,决定着系统中的信息一致性。 编码,是一件简单的事情,但绝对不是一件容易做好的事情&#…

FPGA案例开发手册——基于全志T3+Logos FPGA核心板

前 言 本文档主要提供评估板FPGA端案例测试方法,适用的开发环境为Windows 7 64bit和Windows 10 64bit。 本文案例基于创龙科技的全志T3+Logos FPGA核心板,它是一款基于全志科技T3四核ARM Cortex-A7处理器 + 紫光同创Logos PGL25G/PGL50G FPGA设计的异构多核全国产工业核心板…

PImpl(Pointer to Implementation)指向实现的指针 [使用ChatGPT学习系列]

PImpl是Pointer to Implementation的缩写,也被称为“编译期实现”,是一种C设计的模式。 用于将类的实现细节与其公共接口分离开来。该模式的核心思想是 通过一个指向类的实现的指针来隐藏类的实现细节,从而提高类的封装性和安全性。PImpl是一…

「考研算法」

考研算法 前言 本系列文章涉及的算法内容,针对的是哈尔滨工业大学854科目。在本文中通过具体的算法题进行讲解相应算法。 今天涉及的算法主要有线性筛,十大排序中快速排序和归并排序。 后续会有动态规划的相关算法以及尝试模型的总结,如果…

[Java·算法·中等]LeetCode17. 电话号码的字母组合

每天一题,防止痴呆题目示例分析思路1题解1分析思路2题解2题目 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。…

本科毕业设计-基于ORB SLAM3的多从机SLAM导航系统

耗时:两个月 需求:多从机协作 多地图系统 稠密建图 定位 导航 硬件:二个D435 一台X86主机(CPU:13600kf 内存:32G) X86主机环境:ubuntu18.04 opencv3.2 ROS1 主要代码参考:ORB-SLAM3 主要调用…

【CNN】FractalNet——与DenseNet有异曲同工之妙

FractalNet论文名称:FractalNet: Ultra-Deep Neural Networks without Residuals FractalNet论文下载链接: FractalNet(分型网络),2016年Gustav Larsson首次提出。 (1)分形网络不像resNet那样…

嵌入式 Linux 文件IO操作

目录 Linux 文件操作 1 Linux 系统环境文件操作概念 2 缓冲 IO 文件操作 1 文件的创建,打开与关闭 fopen 函数函数 2 freopen 函数 3、fdopen函数 4、fclose函数 5、格式化读写 6、单个字符读写 7、文件定位 8、标准目录文件 9、非缓冲IO文件操作 Linux 文…

十二、MyBatis的高级映射及延迟加载

1 数据库表的准备 准备数据库表:一个班级对应多个学生。班级表:t_clazz。学生表:t_stu 2 环境搭建 创建模块 打包方式:jar 引入依赖:mybatis依赖、mysql驱动依赖、junit依赖、logback依赖 配置文件:…

C#/.net程序调用python

C#/.net程序调用python C#的优势在于window下的开发,不仅功能强大而且开发周期短。而python则有众多的第三方库,可以避免自己造轮子,利用C#来做界面,而具体实现使用python来实现可以大大提高开发效率。本文介绍如何使用pythonnet…

Kubernetes初始化容器

初始化容器 之前了解了容器的健康检查的两个探针:liveness probe(存活探针)和readiness probe(可读性探针)的使用方法,我们说在这两个探针是可以影响容器的生命周期的,包括我们之前提到的容器的…

如何或者无插件Web页面监控播放软件LiveNVR的固定视频流地址,实现大屏上墙、播放、视频分析等目的

1、LiveNVR介绍 LiveNVR的安防监控的视频直播,可以按标准的Onvif/RTSP协议接入监控设备,也可以通过海康、大华、天地伟业等厂家私有SDK接入监控,实现web页面的播放和录像回放。 可以分发HTTP-FLV、WS-FLV、WebRTC、RTMP、HLS(M3U8)、RTSP等多…

Linux安装Tomcat9

默认Linux已经安装了JDK 并且已经配置好了环境变量 下载链接 Tomcat9 下载完成如下图 ,这个下载完成需要看一下,有的包里bin目录内缺少bootstrap.jar文件,因此下载包的时候要看看bin目录下的是不是有这个文件,如果没有启动Tomcat…

CHAPTER 1 Linux 集群

集群1 集群介绍2 集群分类1. 高可用性集群(High Availability Cluster)HA2. 负载均衡集群(Load Balance Cluster)LB3. 高性能集群(High Performance Computing Cluster)HPC3 HA集群逻辑架构1. 信息层/基础架…

Qt页面菜单栏、工具栏、状态栏

1、菜单栏 QMenu *editMenu ui->menuBar->addMenu("编辑(&E)");2、编辑菜单栏及工具栏内容 QAction *action_copy editMenu->addAction(QIcon("copy.png"),QString("复制(&c)"));action_copy->setShortcut(QKeySequence(…

数学建模竞赛的一些心得体会

1.数学建模经验首先简要的介绍一下我的情况。数学建模我也是在大一暑假开始接触的,之前对其没有任何的了解。我本身对数学也有相对较厚的兴趣,同时我也是计算机专业的学生,因此,我觉得我可参加数学建模的这个比赛。大一的暑假参加…

Linux->进程终止和等待

目录 1. 进程终止场景 1.1 进程退出码 1.2 进程常见退出方式 2. 进程等待 2.1 进程等待的必要性 2.2 进程等待的方式 wait()方式 waitpid()方式 options参数 status参数 1. 进程终止场景 代码运行完毕,结果正确 代码运行完毕,结果不正确 代码异…

【编程架构实践】关于技术栈和架构

架构是什么?老生常谈了。那就看看ChatGPT怎么说:软件架构是软件工程师在设计一个软件系统时,定义系统架构结构的一种科学方法。它指的是软件系统在软件工程师关注功能、性能和安全等质量属性的条件下,组织系统的方式。换句话说&am…

Flask源码篇:wsgi、Werkzeug与Flask启动工作流程

目录1 wsgi介绍2 使用wsgi实现一个web应用3 Werkzeug介绍4 Flask工作流程分析(1)创建Flask app(2)启动Falsk app(3)分析run_simple方法(4)分析make_server方法(5&#xf…