先查看一条数据:
GET /ecommerce/product/5 { "_index" : "ecommerce", "_type" : "product", "_id" : "5", "_version" : 1, "found" : true, "_source" : { "name" : "gaolujie yagao2", "desc" : "gaoxiao meibai2", "price" : 31, "producer" : "gaolujie producer", "tags" : [ "meibai", "fangzhu" ] } }
1、_index元数据
(1)代表一个document存放在哪个index中
(2)类似的数据放在一个索引,非类似的数据放不同索引:product index(包含了所有的商品),sales index(包含了所有的商品销售数据),inventory index(包含了所有库存相关的数据)。如果你把比如product,sales,human resource(employee),全都放在一个大的index里面,比如说company index,不合适的。
2.1)类似的数据放在一个索引,因为这批数据的功能和支持的需求,可能类似,与其他不类似的数据,不在一个shard中,就不会互相影响。
2.2)类似的数据放在一个索引,也会避免了性能问题,假如不同类型的数据放在同一个index中,可能某些数据会有比较复杂的操作,非常耗时,这样会导致读取某些数据的时候很难,有可能超时。
(3)index中包含了很多类似的document:类似是什么意思,其实指的就是说,这些document的fields很大一部分是相同的,你说你放了3个document,每个document的fields都完全不一样,这就不是类似了,就不太适合放到一个index里面去了。
(4)索引名称必须是小写的,不能用下划线开头,不能包含逗号
2、_type元数据
(1)代表document属于index中的哪个类别(type)
(2)一个索引通常会划分为多个type,逻辑上对index中有些许不同的几类数据进行分类:因为一批相同的数据,可能有很多相同的fields,但是还是可能会有一些轻微的不同,可能会有少数fields是不一样的,举个例子,就比如说,商品,可能划分为电子商品,生鲜商品,日化商品,等等。
(3)type名称可以是大写或者小写,但是同时不能用下划线开头,不能包含逗号
3、_id元数据
(1)代表document的唯一标识,与index和type一起,可以唯一标识和定位一个document
(2)我们可以手动指定document的id(put /index/type/id),也可以不指定,由es自动为我们创建一个id
4、关于document id
1、手动指定document id
1)根据应用情况来说,是否满足手动指定document id的前提:
一般来说,是从某些其他的系统中,导入一些数据到es时,会采取这种方式,就是使用系统中已有数据的唯一标识,作为es中document的id。举个例子,比如说,我们现在在开发一个电商网站,做搜索功能,或者是OA系统,做员工检索功能。这个时候,数据首先会在网站系统或者IT系统内部的数据库中,会先有一份,此时就肯定会有一个数据库的primary key(自增长,UUID,或者是业务编号)。如果将数据导入到es中,此时就比较适合采用数据在数据库中已有的primary key。
如果说,我们是在做一个系统,这个系统主要的数据存储就是es一种,也就是说,数据产生出来以后,可能就没有id,直接就放es一个存储,那么这个时候,可能就不太适合说手动指定document id的形式了,因为你也不知道id应该是什么,此时可以采取下面要讲解的让es自动生成id的方式。
(2)自动生成的id,长度为20个字符,URL安全(可以直接放在url中),base64编码,GUID,分布式系统并行生成时不可能会发生冲突
4、_source元数据
_source元数据:就是说,我们在创建一个document的时候,使用的那个放在request body中的json串,默认情况下,在get的时候,会原封不动的给我们返回回来。
5、定制返回结果
定制返回的结果,指定_source中,返回哪些field
PUT /test_index/test_type/1 { "test_field1": "test field1", "test_field2": "test field2" }
GET /test_index/test_type/1?_source=test_field1 { "_index" : "test_index", "_type" : "test_type", "_id" : "1", "_version" : 1, "found" : true, "_source" : { "test_field1" : "test field1" } }
6、document的全量替换
(1)语法与创建文档是一样的,如果document id不存在,那么就是创建;如果document id已经存在,那么就是全量替换操作,替换document的json串内容
(2)document是不可变的,如果要修改document的内容,第一种方式就是全量替换,直接对document重新建立索引,替换里面所有的内容
(3)es会将老的document标记为deleted,然后新增我们给定的一个document,当我们创建越来越多的document的时候,es会在适当的时机在后台自动删除标记为deleted的document
7、document的强制创建
(1)创建文档与全量替换的语法是一样的,有时我们只是想新建文档,不想替换文档,如果强制进行创建呢?
(2)PUT /index/type/id?op_type=create,PUT /index/type/id/_create
8、document的删除
(1)DELETE /index/type/id
(2)不会理解物理删除,只会将其标记为deleted,当数据越来越多的时候,在后台自动删除
9、_version元数据
PUT /test_index/test_type/6 { "test_field": "test test" }
{ "_index" : "test_index", "_type" : "test_type", "_id" : "6", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 0, "_primary_term" : 4 }
第一次创建一个document的时候,它的_version内部版本号就是1;以后,每次对这个document执行修改或者删除操作,都会对这个_version版本号自动加1;哪怕是删除,也会对这条数据的版本号加1
接下来删除该数据
DELETE /test_index/test_type/6 { "_index" : "test_index", "_type" : "test_type", "_id" : "6", "_version" : 2, "result" : "deleted", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 1, "_primary_term" : 4 }
然后在创建该数据
PUT /test_index/test_type/6 { "test_field": "test test" } { "_index" : "test_index", "_type" : "test_type", "_id" : "6", "_version" : 3, "result" : "created", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 3, "_primary_term" : 4 }
我们会发现,在删除一个document之后,可以从一个侧面证明,它不是立即物理删除掉的,因为它的一些版本号等信息还是保留着的。先删除一条document,再重新创建这条document,其实会在delete version基础之上,再把version号加1
关于es后台,多线程异步replica同步并发请求的分析:
10、document的数据路由
(1)什么是document路由到shard上
1.一个index的数据会被分为多片,每片都在一个shard中,所以说,一个document,只能存在于一个shard中
2.当客户端创建document的时候,es此时就需要决定,这个document是放在这个index的哪个shard上的,这个过程,就称为document routing,数据路由
(2)路由算法:shard = hash(routing) % number_of_primary_shards
举个例子,一个index有3个primary shard,P0,P1,P2 每次增删改查一个document的时候,都会带过来一个routing number,默认就是这个document的_id(可能是手动指定,也可能是自动生成) routing = _id,假设_id=1 会将这个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)自定义routing value
默认的routing就是_id 也可以在发送请求的时候,手动指定一个routing value,比如说put /index/type/id?routing=user_id 手动指定routing value是很有用的,可以保证说,某一类document一定被路由到一个shard上去,那么在后续进行应用级别的负载均衡,以及提升批量读取的性能的时候,是很有帮助的
(4)为什么primary shard数量不可变
1.primary shard一旦index建立,是不允许修改的。但是replica shard可以随时修改 2.假设有3个primary shard,我们在某个index下创建一个document,id=1 ,hash=21,此时计算出的shard=21%3=0 3.如果修改为4个primary shard,则此时计算出的shard=21%4=1,结果发现没有找到,就会间接导致数据丢失。
11、关于document的增删改根据coordinating node(协调节点)实现路由原理
(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都搞定之后,就返回响应结果给客户端
11、关于document的写一致性和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都是活跃的,可用的,才可以执行这个写操作
quorum机制:
(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是活跃的,如果你不特殊处理的话,导致我们的单节点集群就无法工作
12、关于document的查询根据coordinating node(协调节点)实现路由原理
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就都有了
分析: