一.document数据路由原理java
(1)document路由到shard上是什么意思?node
(2)路由算法:shard = hash(routing) % number_of_primary_shards算法
举个例子,一个index有3个primary shard,P0,P1,P2json
每次增删改查一个document的时候,都会带过来一个routing number,默认就是这个document的_id(多是手动指定,也多是自动生成)
routing = _id,假设_id=1api
会将这个routing值,传入一个hash函数中,产出一个routing值的hash值,hash(routing) = 21
而后将hash函数产出的值对这个index的primary shard的数量求余数,21 % 3 = 0
就决定了,这个document就放在P0上。数组
决定一个document在哪一个shard上,最重要的一个值就是routing值,默认是_id,也能够手动指定,相同的routing值,每次过来,从hash函数中,产出的hash值必定是相同的性能优化
不管hash值是几,不管是什么数字,对number_of_primary_shards求余数,结果必定是在0~number_of_primary_shards-1之间这个范围内的。0,1,2。数据结构
(3)_id or custom routing value负载均衡
默认的routing就是_id
也能够在发送请求的时候,手动指定一个routing value,好比说put /index/type/id?routing=user_idjvm
手动指定routing value是颇有用的,能够保证说,某一类document必定被路由到一个shard上去,那么在后续进行应用级别的负载均衡,以及提高批量读取的性能的时候,是颇有帮助的
(4)primary shard数量不可变的谜底
二.document增删改查内部原理
1.增删改
(1)客户端选择一个node发送请求过去,这个node就是coordinating node(协调节点)
(2)coordinating node,对document进行路由,将请求转发给对应的node(有primary shard)
(3)实际的node上的primary shard处理请求,而后将数据同步到replica node
(4)coordinating node,若是发现primary node和全部replica node都搞定以后,就返回响应结果给客户端
2.查
(1)客户端发送请求到任意一个node,成为coordinate node
(2)coordinate node对document进行路由,将请求转发到对应的node,此时会使用round-robin随机轮询算法,在primary shard以及其全部replica中随机选择一个,让读请求负载均衡
(3)接收请求的node返回document给coordinate node
(4)coordinate node返回document给客户端
(5)特殊状况:document若是还在创建索引过程当中,可能只有primary shard有,任何一个replica shard都没有,此时可能会致使没法读取到document,可是document完成索引创建以后,primary shard和replica shard就都有了
三.写一致性原理以及quorum机制
(1)consistency,one(primary shard),all(all shard),quorum(default)
咱们在发送任何一个增删改操做的时候,好比说put /index/type/id,均可以带上一个consistency参数,指明咱们想要的写一致性是什么?
put /index/type/id?consistency=quorum
one:要求咱们这个写操做,只要有一个primary shard是active活跃可用的,就能够执行
all:要求咱们这个写操做,必须全部的primary shard和replica shard都是活跃的,才能够执行这个写操做
quorum:默认的值,要求全部的shard中,必须是大部分的shard都是活跃的,可用的,才能够执行这个写操做
(2)quorum机制,写以前必须确保大多数shard均可用,int( (primary + number_of_replicas) / 2 ) + 1,当number_of_replicas>1时才生效
quroum = int( (primary + number_of_replicas) / 2 ) + 1
举个例子,3个primary shard,number_of_replicas=1,总共有3 + 3 * 1 = 6个shard
quorum = int( (3 + 1) / 2 ) + 1 = 3
因此,要求6个shard中至少有3个shard是active状态的,才能够执行这个写操做
(3)若是节点数少于quorum数量,可能致使quorum不齐全,进而致使没法执行任何写操做
3个primary shard,replica=1,要求至少3个shard是active,3个shard按照以前学习的shard&replica机制,必须在不一样的节点上,若是说只有2台机器的话,是否是有可能出现说,3个shard都无法分配齐全,此时就可能会出现写操做没法执行的状况
es提供了一种特殊的处理场景,就是说当number_of_replicas>1时才生效,由于假如说,你就一个primary shard,replica=1,此时就2个shard
(1 + 1 / 2) + 1 = 2,要求必须有2个shard是活跃的,可是可能就1个node,此时就1个shard是活跃的,若是你不特殊处理的话,致使咱们的单节点集群就没法工做
(4)quorum不齐全时,wait,默认1分钟,timeout,100,30s
等待期间,指望活跃的shard数量能够增长,最后实在不行,就会timeout
咱们其实能够在写操做的时候,加一个timeout参数,好比说put /index/type/id?timeout=30,这个就是说本身去设定quorum不齐全的时候,es的timeout时长,能够缩短,也能够增加
四.bulk api的奇特json格式与底层性能优化关系
bulk api奇特的json格式
{"action": {"meta"}}\n
{"data"}\n
{"action": {"meta"}}\n
{"data"}\n
[{
"action": {
},
"data": {
}
}]
一、bulk中的每一个操做均可能要转发到不一样的node的shard去执行
二、若是采用比较良好的json数组格式
容许任意的换行,整个可读性很是棒,读起来很爽,es拿到那种标准格式的json串之后,要按照下述流程去进行处理
(1)将json数组解析为JSONArray对象,这个时候,整个数据,就会在内存中出现一份如出一辙的拷贝,一份数据是json文本,一份数据是JSONArray对象
(2)解析json数组里的每一个json,对每一个请求中的document进行路由
(3)为路由到同一个shard上的多个请求,建立一个请求数组
(4)将这个请求数组序列化
(5)将序列化后的请求数组发送到对应的节点上去
三、耗费更多内存,更多的jvm gc开销
咱们以前提到过bulk size最佳大小的那个问题,通常建议说在几千条那样,而后大小在10MB左右,因此说,可怕的事情来了。假设说如今100个bulk请求发送到了一个节点上去,而后每一个请求是10MB,100个请求,就是1000MB = 1GB,而后每一个请求的json都copy一份为jsonarray对象,此时内存中的占用就会翻倍,就会占用2GB的内存,甚至还不止。由于弄成jsonarray以后,还可能会多搞一些其余的数据结构,2GB+的内存占用。
占用更多的内存可能就会积压其余请求的内存使用量,好比说最重要的搜索请求,分析请求,等等,此时就可能会致使其余请求的性能急速降低
另外的话,占用内存更多,就会致使java虚拟机的垃圾回收次数更多,跟频繁,每次要回收的垃圾对象更多,耗费的时间更多,致使es的java虚拟机中止工做线程的时间更多
四、如今的奇特格式
{"action": {"meta"}}\n
{"data"}\n
{"action": {"meta"}}\n
{"data"}\n
(1)不用将其转换为json对象,不会出现内存中的相同数据的拷贝,直接按照换行符切割json
(2)对每两个一组的json,读取meta,进行document路由
(3)直接将对应的json发送到node上去
五、最大的优点在于,不须要将json数组解析为一个JSONArray对象,造成一份大数据的拷贝,浪费内存空间,尽量地保证性能