Es官方文档整理-2.分片内部原理

Es官方文档整理-2.分片内部原理

1.集群

一个运行的Elasticsearch实例被称为一个节点,而集群是有一个或多个拥有相同claster.name配置的节点组成,他们共同承担数据和负载压力,当有节点加入或从集群中移除的时候,集群或自动平局分布所有数据。

当一个节点被选举成为主节点时,他哈不负责额管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。而主节点不涉及文档级别的变更和搜索操作,所以集群只有一个主节点,即使流量增加,他也不会成为瓶颈。任何节点都可以成为主节点。

3.添加索引

PUT /blogs

{

"settings":{

"number_of_shards":3,

"number_of_replicas":1

}

}

设置三个主分片,1个复制分片。

4.水平扩展

PUT /blogs/_settings

{

"number_of_replicas":2

}

复制分片从一个增加到两个。不能修改主分片个数。

5.路由一个文档到分片

当索引一个文档时,文档会被存储到主分片中。存到哪个主分片由下面这个公式决定:

shard = hash(routing) % number_of_primary_shards.

routing是一个可变值,默认是文档_id,也可以设置一个自定义的值。

number_of_primary_shards主分片数。

这里解释了为什么不能改变主分片数量:因为如果数量变化了,那么所有的之前路由的值都会无效,文档也找不到了。

6.新建、索引和删除单个文档

1).客户端向集群中的一个节点(协调节点)发送请求。

2).节点使用文档的_id确定文档所属的分片,转发请求到分片所属的节点(master).

3).主分片执行请求。若执行成功,将请求并行转发到复制节点。一旦所有复制节点报告执行成功。master节点向协调节点报告成功,协调节点向客户端报告成功。

7.检索单个文档

1).客户端向集群中的一个节点(node1)发出请求。

2).node1节点使用_id确定文档所属分片,将请求转发大其中个一个分片所属节点(node2)。

3).node2将文档返回给node1,然后将文档返回给客户端。

协调节点每次请求的时候选择不同的副本分片达到负载均衡。

8.局部更新

1).客户端向node1发送更新请求。

2).转发请求到主分片所在的node2。

3).node2从主分片检索文档,修改_source字段中的JSON,并尝试重新索引主分片文档。如果文档已经被另一个进程修改了,他将重试步骤3,超过retry_on_conflict次后放弃。

4).如果node2成功更新索引,它将新版本的文档转发到所有副本分片所在的节点,重新建立索引。一旦所有副本分片都成功返回了,node2向协调节点返回成功,协调节点向客户端返回成功。

9.倒排索引

Elasticsearch使用一种称为倒排索引的结构,它适用于快速的全文搜索。一个倒排索引有文档中所有不重复的词的列表构成,对于其中每个词,有一个包含他的文档列表。类似下面:(被索引的字段都有自己的倒排索引)

Term  | Doc 1 | Doc 2 | Doc 3 | ...

------------------------------------

brown |   X   |        |  X    | ...

fox      |   X   |   X   |  X    | ...

quick   |   X   |   X   |        | ...

the      |   X   |        |  X    | ...

它会保存每一个词项出现过的文档总数, 在对应的文档中一个具体词项出现的总次数,词项在文档中的顺序,每个文档的长度,所有文档的平均长度,等等。

不变性:

倒排索引被芯茹磁盘后是不可改变的:它永远不会被修改。不可修改的意义:

1).不需要锁。

2).写入单个大的倒排索引允许被压缩。

3).一旦索引被读入内核的文件系统缓存,便会留在哪里,由于其不变性。只要文件系统缓存中还有足够的空间,那么大部分读请求会直接请求内存,而不会命中磁盘。这提供了很大的性能提升。

4).其它缓存(像filter缓存),在索引的生命周期内始终有效。它们不需要在每次数据改变时被重建,因为数据不会变化。

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

10.动态更新索引

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

索引与分片的比较:

被混淆的概念是,一个 Lucene 索引 我们在 Elasticsearch 称作 分片 。 一个 Elasticsearch 索引 是分片的集合。 当 Elasticsearch 在索引中搜索的时候, 他发送查询到每一个属于索引的分片(Lucene 索引),然后像 执行分布式检索 提到的那样,合并每个分片的结果到一个全局的结果集。

一个 Lucene 索引包含一个提交点和三个段:

一个在内存缓存中包含新文档的 Lucene 索引:

在一次提交后,一个新的segment被添加到提交点而且缓存被清空:

1).新文档被收集到内存索引缓存, “一个在内存缓存中包含新文档的 Lucene 索引” 。

2).不时地, 缓存被 提交 :

一个新的segment --一个追加的倒排索引--被写入磁盘。

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

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

3).新的segment 被开启,让它包含的文档可见以被搜索。

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

删除和更新:

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

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

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

11.近实时搜索

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

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

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

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

在内存缓冲区中包含了新文档的 Lucene 索引:

缓冲区的内容已经被写入一个可被搜索的段中,但还没有进行提交:

Refresh API

在 Elasticsearch 中,写入和打开一个新段的轻量的过程叫做 refresh 。 默认情况下每个分片会每秒自动刷新一次。这就是为什么我们说 Elasticsearch 是 近 实时搜索: 文档的变化并不是立即对搜索可见,但会在一秒之内变为可见。

POST /_refresh :刷新(Refresh)所有的索引。

POST /blogs/_refresh :只刷新(Refresh) blogs 索引。

并不是所有的情况都需要每秒刷新。可能你正在使用 Elasticsearch 索引大量的日志文件, 你可能想优化索引速度而不是近实时搜索, 可以通过设置 refresh_interval , 降低每个索引的刷新频率:

PUT /my_logs

{

"settings": {

"refresh_interval": "30s"

}

}

在生产环境中,当你正在建立一个大的新索引时,可以先关闭自动刷新,待开始使用该索引时,再把它们调回来:

PUT /my_logs/_settings

{ "refresh_interval": -1 }

PUT /my_logs/_settings

{ "refresh_interval": "1s" }

12.持久化变更

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

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

1).一个文档被索引之后,就会被添加到内存缓冲区,并且 追加到了 translog.

2).刷新(refresh)使分片,分片每秒被刷新(refresh)一次:

a.这些在内存缓冲区的文档被写入到一个新的段中,且没有进行 fsync 操作。   ** 这个段被打开,使其可被搜索。

b.内存缓冲区被清空。

3).这个进程继续工作,更多的文档被添加到内存缓冲区和追加到事务日志.

4).每隔一段时间--例如 translog 变得越来越大--索引被刷新(flush);一个新的 translog 被创建,并且一个全量提交被执行:

a.所有在内存缓冲区的文档都被写入一个新的段。

b.缓冲区被清空。

c.一个提交点被写入硬盘。

d.文件系统缓存通过 fsync 被刷新(flush)。

e.老的 translog 被删除。

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

Flush API

这个执行一个提交并且截断 translog 的行为在 Elasticsearch 被称作一次 flush 。 分片每30分钟被自动刷新(flush),或者在 translog 太大的时候也会刷新。可以手动刷新:

POST /blogs/_flush

POST /_flush?wait_for_ongoing

Translog 的安全问题:默认,在每次请求后都会通过fsync把请求写到日志文件。你也可以使用异步的 fsync ,写入的数据被缓存到内存中,每5秒执行一次 fsync :

PUT /my_index/_settings

{

"index.translog.durability": "async",

"index.translog.sync_interval": "5s"

}

13.segment合并

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

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

segment 合并的时候会将那些旧的已删除文档 从文件系统中清除。 被删除的文档(或被更新文档的旧版本)不会被拷贝到新的大segment 中。

启动segment 合并不需要你做任何事。进行索引和搜索时会自动进行。

1).当索引的时候,刷新(refresh)操作会创建新的segment 并将segment 打开以供搜索使用。

2).合并进程选择一小部分大小相似的segment ,并且在后台将它们合并到更大的segment 中。这并不会中断索引和搜索。

3).一旦合并结束,老的段被删除, 说明合并是完成的活动:

a.新的段被刷新(flush)到了磁盘。   ** 写入一个包含新段且排除旧的和较小的段的新提交点。

b.新的段被打开用来搜索。

c.老的段被删除。

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

Optimize API

optimize API大可看做是 强制合并 API 。它会将一个分片强制合并到 max_num_segments 参数指定大小的段数目。 这样做的意图是减少段的数量(通常减少到一个),来提升搜索性能。

optimize API 不应该 被用在一个动态索引————一个正在被活跃更新的索引。后台合并流程已经可以很好地完成工作。 optimizing 会阻碍这个进程。不要干扰它!

在特定情况下,使用 optimize API 颇有益处。例如在日志这种用例下,每天、每周、每月的日志被存储在一个索引中。 老的索引实质上是只读的;它们也并不太可能会发生变化。在这种情况下,使用optimize优化老的索引,将每一个分片合并为一个单独的段就很有用了;这样既可以节省资源,也可以使搜索更加快速:

POST /logstash-2014-10/_optimize?max_num_segments=1

请注意,使用 optimize API 触发段合并的操作一点也不会受到任何资源上的限制。这可能会消耗掉你节点上全部的I/O资源, 使其没有余裕来处理搜索请求,从而有可能使集群失去响应。 如果你想要对索引执行 `optimize`,你需要先使用分片分配(查看 迁移旧索引)把索引移到一个安全的节点,再执行。

原文地址:https://www.cnblogs.com/ydxblog/p/8304266.html

时间: 2024-11-10 23:44:45

Es官方文档整理-2.分片内部原理的相关文章

3-2 官方文档整理-视图层-WXSS

[参考内容]:可参考W3C School 中CSS部分:链接地址: http://www.w3school.com.cn/h.asp [作用] WXSS(WeiXin Style Sheets)是一套样式语言,用于描述 WXML 的组件样式 WXSS 用来决定 WXML 的组件应该怎么显示. [尺寸单位] rpx(responsive pixel): 可以根据屏幕宽度进行自适应.规定屏幕宽为750rpx.如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 3

3-1 官方文档整理-视图层-WXML文件

[视图层文件组成]: 视图层由 WXML 与 WXSS 编写,由组件来进行展示. WXML(WeiXin Markup language) 用于描述页面的结构. WXS(WeiXin Script) 是小程序的一套脚本语言,结合 WXML,可以构建出页面的结构. WXSS(WeiXin Style Sheet) 用于描述页面的样式. [WXML] [数据绑定]:数据绑定语法:使用双大括号将需要替换的对象括起来,下面的代码,界面文件中的{{message}}会替换为"Hello MINA"

【苦读官方文档】2.Android应用程序基本原理概述

官方文档原文地址 应用程序原理 Android应用程序是通过Java编程语言来写.Android软件开发工具把你的代码和其它数据.资源文件一起编译.打包成一个APK文件,这个文档以.apk为后缀,保存了一个Android应用程序全部的内容.Android设备通过它来安装相应的应用. 一旦安装到设备上.每一个Android应用程序就执行在各自独立的安全沙盒中: Android系统是一个多用户的Linux系统.每一个应用都是一个用户. Android系统默认会给每一个应用分配一个唯一的用户ID(这个

OpenGL ES着色器语言之变量和数据类型(一)(官方文档第四章)和varying,uniform,attribute修饰范围

OpenGL ES着色器语言之变量和数据类型(一)(官方文档第四章)   所有变量和函数在使用前必须声明.变量和函数名是标识符. 没有默认类型,所有变量和函数声明必须包含一个声明类型以及可选的修饰符.变量在声明的时候首先要标明类型,后边可以跟多个变量,之间用逗号隔开.很多情况下,变量在声明的时候可以使用等号“=”进行初始化. 用户定义类型可以使用struct,在结构体中所有变量类型都必须是OpenGL ES着色器语言定义的关键字.OpenGL ES着色语言是类型安全的,因此不支持隐式类型转换.

OpenGL ES着色器语言之语句和结构体(官方文档第六章)内建变量(官方文档第七、八章)

OpenGL ES着色器语言之语句和结构体(官方文档第六章) OpenGL ES着色器语言的程序块基本构成如下: 语句和声明 函数定义 选择(if-else) 迭代(for, while, do-while) 跳跃(discard, return, break, continue) 6.1函数定义   着色器是由一系列全局声明和函数定义组成的.函数声明规范如下: // prototype returnType functionName (type0 arg0, type1 arg1, ...,

OpenGL ES着色器语言之着色概览(官方文档)

OpenGL ES着色器语言之着色概览(官方文档第二章) 事实上,OpenGL ES着色语言是两种紧密关联的语言.这些语言用来在OpenGL ES处理管线的可编程处理器创建着色器. 在本文档中,除非另外说明,一个语言功能适用于所有语言,并且通用用法将把他们当做一个语言来看待.特定语言将指出它们的目标处理器:顶点(vertext)或片元(fragment). 任何被着色器使用的OpenGL ES状态值都会自动地被跟踪并且作用于着色器上.这个自动状态跟踪机制允许应用程序为状态管理而使用OpenGL

微信小程序官方文档错误整理

大致看了一遍微信小程序文档,发现有几处微小的错误,但瑕不掩瑜.记录下,以后发现了还会继续在此添加.如果有记录不对的,请及时指出错误. 1.视图层->WXSS->尺寸单位 明显错误,应该为 1px=2.34rpx 2.视图层->WXML->引用->include描述 有错别字 3.视图层->WXML->事件->事件绑定 "点击 inner view 会先后触发handleTap1和handleTap2",个人感觉应该是先后触发handleT

OpenGL ES着色器语言之操作数(官方文档第五章)

OpenGL ES着色器语言之操作数(官方文档第五章) 5.1操作数 OpenGL ES着色器语言包含如下操作符. 5.2数组下标 数组元素通过数组下标操作符([ ])进行访问.这是操作数组的唯一操作符,举个访问数组元素的例子: diffuseColor += lightIntensity[3] * NdotL; 5.3函数调用 如果一个函数有返回值,那么通常这个函数调用会用在表达式中. 5.4构造器 构造器使用函数调用语法,函数名是一个基本类型的关键字或者结构体名字,在初始化器或表达式中使用.

OpenGL ES着色器语言之变量和数据类型(二)(官方文档第四章)

OpenGL ES着色器语言之变量和数据类型(二)(官方文档第四章) 4.5精度和精度修饰符 4.5.1范围和精度 用于存储和展示浮点数.整数变量的范围和精度依赖于数值的源(varying,uniform,纹理查找,等等),是不是顶点或者片元着色器,还有其他一些底层实现的细节.最低存储需要通过精度修饰符来声明.典型地,精度操作必须要保留变量包含的精度存储.仅有的例外是需要大量复杂计算的内建函数,如atan(),返回值的精度低于声明的精度. 强烈建议顶点语言提供一种匹配IEEE单精度浮点数或更高精