elasticsearch 基础 —— 分布式文档存储原理
路由一个文档到一个分片中
当索引一个文档的时候,文档会被存储到一个主分片中。 Elasticsearch 如何知道一个文档应该存放到哪个分片中呢?当我们创建文档时,它如何决定这个文档应当被存储在分片 1
还是分片 2
中呢?
首先这肯定不会是随机的,否则将来要获取文档的时候我们就不知道从何处寻找了。实际上,这个过程是根据下面这个公式决定的:
shard = hash(routing) % number_of_primary_shards
routing
是一个可变值,默认是文档的 _id
,也可以设置成一个自定义的值。 routing
通过 hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards
(主分片的数量)后得到 余数 。这个分布在 0
到 number_of_primary_shards-1
之间的余数,就是我们所寻求的文档所在分片的位置。
这就解释了为什么我们要在创建索引的时候就确定好主分片的数量 并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了。
你可能觉得由于 Elasticsearch 主分片数量是固定的会使索引难以进行扩容。实际上当你需要时有很多技巧可以轻松实现扩容。我们将会在扩容设计一章中提到更多有关水平扩展的内容。
所有的文档 API( get
、 index
、 delete
、 bulk
、 update
以及 mget
)都接受一个叫做 routing
的路由参数 ,通过这个参数我们可以自定义文档到分片的映射。一个自定义的路由参数可以用来确保所有相关的文档——例如所有属于同一个用户的文档——都被存储到同一个分片中。我们也会在扩容设计这一章中详细讨论为什么会有这样一种需求。
主分片和副本分片如何交互
为了说明目的, 我们 假设有一个集群由三个节点组成。 它包含一个叫 blogs
的索引,有两个主分片,每个主分片有两个副本分片。相同分片的副本不会放在同一节点,所以我们的集群看起来像 图 8 “有三个节点和一个索引的集群”。
图 8. 有三个节点和一个索引的集群
我们可以发送请求到集群中的任一节点。 每个节点都有能力处理任意请求。 每个节点都知道集群中任一文档位置,所以可以直接将请求转发到需要的节点上。 在下面的例子中,将所有的请求发送到 Node 1
,我们将其称为 协调节点(coordinating node) 。
当发送请求的时候, 为了扩展负载,更好的做法是轮询集群中所有的节点。
新建、索引和删除文档
新建、索引和删除 请求都是 写 操作, 必须在主分片上面完成之后才能被复制到相关的副本分片,如下图所示 图 9 “新建、索引和删除单个文档”.
图 9. 新建、索引和删除单个文档
以下是在主副分片和任何副本分片上面 成功新建,索引和删除文档所需要的步骤顺序:
客户端向
Node 1
发送新建、索引或者删除请求。节点使用文档的
_id
确定文档属于分片 0 。请求会被转发到Node 3`,因为分片 0 的主分片目前被分配在 `Node 3
上。Node 3
在主分片上面执行请求。如果成功了,它将请求并行转发到Node 1
和Node 2
的副本分片上。一旦所有的副本分片都报告成功,Node 3
将向协调节点报告成功,协调节点向客户端报告成功。
在客户端收到成功响应时,文档变更已经在主分片和所有副本分片执行完成,变更是安全的。
有一些可选的请求参数允许您影响这个过程,可能以数据安全为代价提升性能。这些选项很少使用,因为Elasticsearch已经很快,但是为了完整起见,在这里阐述如下:
consistency
consistency,即一致性。在默认设置下,即使仅仅是在试图执行一个_写_操作之前,主分片都会要求必须要有 _规定数量(quorum)_(或者换种说法,也即必须要有大多数)的分片副本处于活跃可用状态,才会去执行_写_操作(其中分片副本可以是主分片或者副本分片)。这是为了避免在发生网络分区故障(network partition)的时候进行_写_操作,进而导致数据不一致。_规定数量_即:
int( (primary + number_of_replicas) / 2 ) + 1
consistency
参数的值可以设为 one
(只要主分片状态 ok 就允许执行_写_操作),all`(必须要主分片和所有副本分片的状态没问题才允许执行_写_操作), 或 `quorum
。默认值为 quorum
, 即大多数的分片副本状态没问题就允许执行_写_操作。
注意,规定数量 的计算公式中 number_of_replicas
指的是在索引设置中的设定副本分片数,而不是指当前处理活动状态的副本分片数。如果你的索引设置中指定了当前索引拥有三个副本分片,那规定数量的计算结果即:
int( (primary + 3 replicas) / 2 ) + 1 = 3
如果此时你只启动两个节点,那么处于活跃状态的分片副本数量就达不到规定数量,也因此您将无法索引和删除任何文档。
timeout
如果没有足够的副本分片会发生什么? Elasticsearch会等待,希望更多的分片出现。默认情况下,它最多等待1分钟。 如果你需要,你可以使用 timeout
参数 使它更早终止: 100
100毫秒,30s
是30秒。
新索引默认有
1
个副本分片,这意味着为满足规定数量
应该 需要两个活动的分片副本。 但是,这些默认的设置会阻止我们在单一节点上做任何事情。为了避免这个问题,要求只有当number_of_replicas
大于1的时候,规定数量才会执行。
取回一个文档
可以从主分片或者从其它任意副本分片检索文档 ,如下图所示 图 10 “取回单个文档”.
图 10. 取回单个文档
以下是从主分片或者副本分片检索文档的步骤顺序:
- 客户端向
Node 1
发送获取请求。 - 节点使用文档的
_id
来确定文档属于分片0
。分片0
的副本分片存在于所有的三个节点上。 在这种情况下,它将请求转发到Node 2
。 Node 2
将文档返回给Node 1
,然后将文档返回给客户端。
在处理读取请求时,协调结点在每次请求的时候都会通过轮询所有的副本分片来达到负载均衡。
在文档被检索时,已经被索引的文档可能已经存在于主分片上但是还没有复制到副本分片。 在这种情况下,副本分片可能会报告文档不存在,但是主分片可能成功返回文档。 一旦索引请求成功返回给用户,文档在主分片和副本分片都是可用的。
局部更新文档
如 图 11 “局部更新文档” 所示,update
API 结合了先前说明的读取和写入模式 。
图 11. 局部更新文档
以下是部分更新一个文档的步骤:
- 客户端向
Node 1
发送更新请求。 - 它将请求转发到主分片所在的
Node 3
。 Node 3
从主分片检索文档,修改_source
字段中的 JSON ,并且尝试重新索引主分片的文档。 如果文档已经被另一个进程修改,它会重试步骤 3 ,超过retry_on_conflict
次后放弃。- 如果
Node 3
成功地更新文档,它将新版本的文档并行转发到Node 1
和Node 2
上的副本分片,重新建立索引。 一旦所有副本分片都返回成功,Node 3
向协调节点也返回成功,协调节点向客户端返回成功。
update
API 还接受在 新建、索引和删除文档 章节中介绍的 routing
、 replication
、 consistency
和 timeout
参数。
基于文档的复制
当主分片把更改转发到副本分片时, 它不会转发更新请求。 相反,它转发完整文档的新版本。请记住,这些更改将会异步转发到副本分片,并且不能保证它们以发送它们相同的顺序到达。 如果Elasticsearch仅转发更改请求,则可能以错误的顺序应用更改,导致得到损坏的文档。
多文档模式
mget
和 bulk
API 的 模式类似于单文档模式。区别在于协调节点知道每个文档存在于哪个分片中。 它将整个多文档请求分解成 每个分片 的多文档请求,并且将这些请求并行转发到每个参与节点。
协调节点一旦收到来自每个节点的应答,就将每个节点的响应收集整理成单个响应,返回给客户端,如 图 12 “使用 mget
取回多个文档” 所示。
图 12. 使用 mget
取回多个文档
以下是使用单个 mget
请求取回多个文档所需的步骤顺序:
- 客户端向
Node 1
发送mget
请求。 Node 1
为每个分片构建多文档获取请求,然后并行转发这些请求到托管在每个所需的主分片或者副本分片的节点上。一旦收到所有答复,Node 1
构建响应并将其返回给客户端。
可以对 docs
数组中每个文档设置 routing
参数。
bulk API, 如 图 13 “使用 bulk
修改多个文档” 所示, 允许在单个批量请求中执行多个创建、索引、删除和更新请求。
图 13. 使用 bulk
修改多个文档
bulk
API 按如下步骤顺序执行:
- 客户端向
Node 1
发送bulk
请求。 Node 1
为每个节点创建一个批量请求,并将这些请求并行转发到每个包含主分片的节点主机。- 主分片一个接一个按顺序执行每个操作。当每个操作成功时,主分片并行转发新文档(或删除)到副本分片,然后执行下一个操作。 一旦所有的副本分片报告所有操作成功,该节点将向协调节点报告成功,协调节点将这些响应收集整理并返回给客户端。
bulk
API 还可以在整个批量请求的最顶层使用 consistency
参数,以及在每个请求中的元数据中使用 routing
参数。
为什么是有趣的格式?
当我们早些时候在代价较小的批量操作章节了解批量请求时, 您可能会问自己, "为什么 bulk
API 需要有换行符的有趣格式,而不是发送包装在 JSON 数组中的请求,例如 mget
API?" 。
为了回答这一点,我们需要解释一点背景:在批量请求中引用的每个文档可能属于不同的主分片, 每个文档可能被分配给集群中的任何节点。这意味着批量请求 bulk
中的每个 操作 都需要被转发到正确节点上的正确分片。
如果单个请求被包装在 JSON 数组中,那就意味着我们需要执行以下操作:
- 将 JSON 解析为数组(包括文档数据,可以非常大)
- 查看每个请求以确定应该去哪个分片
- 为每个分片创建一个请求数组
- 将这些数组序列化为内部传输格式
- 将请求发送到每个分片
这是可行的,但需要大量的 RAM 来存储原本相同的数据的副本,并将创建更多的数据结构,Java虚拟机(JVM)将不得不花费时间进行垃圾回收。
相反,Elasticsearch可以直接读取被网络缓冲区接收的原始数据。 它使用换行符字符来识别和解析小的 action/metadata
行来决定哪个分片应该处理每个请求。
这些原始请求会被直接转发到正确的分片。没有冗余的数据复制,没有浪费的数据结构。整个请求尽可能在最小的内存中处理。
elasticsearch 基础 —— 分布式文档存储原理的更多相关文章
- ElasticSearch 学习记录之 分布式文档存储往ES中存数据和取数据的原理
分布式文档存储 ES分布式特性 屏蔽了分布式系统的复杂性 集群内的原理 垂直扩容和水平扩容 真正的扩容能力是来自于水平扩容–为集群添加更多的节点,并且将负载压力和稳定性分散到这些节点中 ES集群特点 ...
- ElasticSearch 5学习(8)——分布式文档存储(wait_for_active_shards新参数分析)
学完ES分布式集群的工作原理以及一些基本的将数据放入索引然后检索它们的所有方法,我们可以继续学习在分布式系统中,每个分片的文档是被如何索引和查询的. 路由 首先,我们需要明白,文档和分片之间是如何匹配 ...
- ElasticSearch文档及分布式文档存储
1.什么是文档? 文档由索引(_index),类型(_type),唯一标识(_id) 组成,我们为 _index(索引) 分配相关逻辑地址分片,该索引下的数据会根据索引以及类型计算哈希来分配数据存储的 ...
- ElasticSearch权威指南学习(分布式文档存储)
路由文档到分片 当你索引一个文档,它被存储在单独一个主分片上.Elasticsearch是如何知道文档属于哪个分片的呢?当你创建一个新文档,它是如何知道是应该存储在分片1还是分片2上的呢? 进程不能是 ...
- 分布式文档存储数据库之MongoDB基础入门
一.MongoDB简介 MongoDB是用c++语言开发的一款易扩展,易伸缩,高性能,开源的,schema free 的基于文档的nosql数据库:所谓nosql是指不仅仅是sql的意思,它拥有部分s ...
- Elasticsearch 分布式文档存储
shard = hash(routing) % number_of_primary_shards决定文档在哪个分片上,routing 是一个可变值,默认是文档的 _id ,也可以设置成一个自定义的值. ...
- 分布式文档存储数据库 MongoDB
MongoDB 详细介绍 MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的.他支持的数据结构非常松散,是类似json的bjson格式,因此可以 ...
- 分布式文档存储数据库之MongoDB索引管理
前文我们聊到了MongoDB的简介.安装和对collection的CRUD操作,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/13941797.html:今天我 ...
- 分布式文档存储数据库之MongoDB副本集
前文我们聊到了mongodb的索引的相关作用和介绍以及索引的管理,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/13950287.html:今天我们来聊下mon ...
随机推荐
- spring boot generator
pom.xml 插件引用依赖 <build> <plugins> <plugin> <groupId>org.springframework.boot& ...
- [题目] 4座塔的Hanoi
题目地址 经典递推题. 解出 n (1<=n<=12) 个盘子 \(4\) 座塔的Hanoi(汉诺塔)问题最少需多少步?(1到12每个答案分别占一行) 题解 在原Hanoi问题中 \(d[ ...
- bzoj4811 [Ynoi2017]由乃的OJ 树链剖分+贪心+二进制
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4811 题解 我现在为什么都写一题,调一天啊,马上真的退役不花一分钱了. 考虑这道题的弱化版 N ...
- RMQ Terminology
原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/11784644.html RMQ模型架构 RMQ Terminology Message 消息,消息是不 ...
- shell第一个脚本
mkdir 创建目录touch 创建空文件 chmod +x ./test.sh #使脚本具有执行权限
- Django2 + python3 上传图片
. ├── db.sqlite3 ├── manage.py ├── myImg │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init_ ...
- swift中为什么要创造出可选型?
(1)因为nil这个东西,swift中没有就是没有. Int? 叫 整型可选型,如果不提前声明,直接赋值变量 nil会报错 . 可以将Int赋值给Int? ,但是不能将Int?赋值给Int . ...
- elasticsearch6.8.1 x-pack插件破解
一.为什么要破解x-pack? 因为涉及到了ES服务的安全性.ES服务如果被劫持,数据直接会被删除.ES登录账号和密码的设置是通过x-pack来实现的,官方只给了免费的30天的使用权,而且 ...
- NOIp 数学知识点总结
推荐阅读 NOIp 基础数论知识点总结: https://www.cnblogs.com/greyqz/p/number.html 排列组合 常用公式 排列:\[\displaystyle A_n^m ...
- Jquery浅析
目录 jquery 通过jquery改变标签字体颜色 jquery和js对象之间值转化 Jquery基本选择器 Jquery层级选择器 基本筛选器 操作类属性 模太框 表单筛选器 筛选器方法 设置多个 ...