目录
ID生成介绍
映射也就是mapping,用来定义一个文档以及其所包含的字段如何被存储和索引,可以在映射中事先定义字段的数据类型、分词等属性。自动生成id,和手动生成id的适应场景:
-
手动指定ID
从某些其他的系统中,导入一些数据到es时,会采取这种方式,就是使用系统中已有数据的唯一标识,作为es中document的id。比如说,我们现在在开发一个电商网站,做搜索功能。这个时候,数据首先会在网站系统或者IT系统内部的数据库中,会先有一份数据,此时需要把数据导入es这时比较适合用库里的id。
POST user_info/_doc/1
{
"name":"张三",
"birthdate":"1988-09-01"
}
可以看到右侧id为1。
-
自动生成ID
如果数据直接存入es,那么这个时候,可能就不太适合说手动指定document id的形式了,因为你也不知道id应该是什么,此时可以采取下面要讲解的让es自动生成id的方式。
POST user_info/_doc
{
"name":"张三",
"birthdate":"1988-09-01"
}
可以看到右侧id为 JWCVJXkB-WsrqdxVu0fQ
VERSION介绍
es在创建索引的过程中会为每个文档创建一个version变量,这个变量是用于解决冲突用的。es在更新文档时是一次读取原生文档,做修改然后重新索引整个文档,最先请求将成功,此时文档的versinon会加1,如果在同一时刻其他人也修改了该文档,他们的修改将丢失。在数据库领域中,有两种方法通常备用来确保并发更新时变更不会丢失:
1、悲观并发控制
这种方法被关系型数据库广泛使用,它假定有变更冲突可能发生,因此阻塞访问资源以防止冲突。一个典型的例子是读取一行数据之前先将其锁住,确保只有放置锁的线程能够对这行数据进行修改。
2、乐观并发控制
Elasticsearch 中使用的这种方法,它假定冲突是不可能发生的,所以不会阻塞正在尝试的操作。然而,如果源数据在读写当中被修改version与原来的值不一致,更新将会失败。应用程序接下来将决定该如何解决冲突。可以设置重试更新次数、或者将相关情况报告给用户即抛出异常。
-
内部版本号
使用内部版本号是要求指定的version字段和当前的version号相同,,当文档被修改时版本号递增。ElasticSearch使用这个_version确保变更以正确的顺序得到执行。如果旧版本的文档在新版本之后到达,它可以被简单的忽略。我们可以用_version来确保应用中相互冲突的变更不会导致数据的丢失。我们通过指定想要修改文档的_version来达到这个目的。如果该版本不是当前版本号,我们请求将失败
POST /user_info/user_info?version=1
version可以不用写,默认从1开始,每修改一次version将递增。
可以到右侧_version的值为1
-
外部版本号
version_type:外部版本号校验类型,有两种:external(默认,外部版本号必须要大于内部版本号),external_gte(外部版本号大于等于内部版本号),如果请求成功,外部版本号作为文档新的version号进行存储。
POST /user_info/user_info?version=5&version_type=external
-
op_type
es默认添加数据时es的ID相同会更新,不同会创建。op_type 参数可以指定为create来强制执行创建操作,此操作是不存在则创建,如果指定id的文档已经存在,则此操作失败;也可以通过_create指定此值。
op_type=create 或_create
文档路由原理
Elasticsearch中每个index由多个shard组成,7.0之后,默认主分片是1个可修改。shard分为主分片和副本分片,在文档写入时,会根据_routing来计算(OperationRouting类)得出文档要写入哪个分片。这里的写入请求只会写主分片,当主分片写入成功后,会同时把写入请求发送给所有的副本分片,当副本分片写入成功后,会传回返回信息给主分片,主分片得到所有副本分片的返回信息后,再返回给客户端。
-
默认路由机制
保存文档时,文档会通过一个公式路由到一个索引中的一个分片上。默认的公式如下:shard_num = hash(_routing) % num_primary_shards
默认情况下时通过_routing字段进行路由的,这个字段的值默认时等于文档_id字段的。
routing 通过 hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards (主分片的数量)后得到 余数 。这个分布在 0 到 number_of_primary_shards-1 之间的余数,就是我们所寻求的文档所在分片的位置。这就解释了为什么我们要在创建索引的时候就确定好主分片的数量 并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了
-
自定义路由
自定义路由比较简单,如下,一旦设置routing参数将替换_id字段路由文档。
POST my_index/my_type/1?routing=user1&refresh=true
{
"title": "This is a document"
}
GET my_index/my_type/1?routing=user1
如果不设置路由,每个一个查询都会去搜索索引中的全部分片,所以自定义路由可以提高搜索性能(只搜索路由指定的分片)。在索引的时候只能指定一个路由,但搜索的时候可以指定一个或多个路由,这样可以大大提高搜索的灵魂性,如下:
POST my_index/_search?routing=user1,user2
{
"query": {
"match": {
"title": "document"
}
}
}
文档写一致性
es的一个操作请求比如:增删改操作时put /index/type/id,都可以带上一个consistency参数,指明我们想要的写一致性是什么。
POST /index/_doc/id?consistency=quorum
one:要求我们这个写操作,只要有一个primary shard是active活跃可用的,就可以执行
all:要求我们这个写操作,必须所有的primary shard和replica shard都是活跃的,才可以执行这个写操作
quorum:默认的值,要求所有的shard中,必须是大部分的shard都是活跃的,可用的,才可以执行这个写操作
-
quorum
在写之前必须确保大多数shard都可用,int(primary + number_of_replicas)/2)+1,当number_of_replicas>1时才生效quorum = 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状态的,才可以执行这个写操作,如果节点数少于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是活跃的,如果你不特殊处理的话,导致我们的单节点集群就无法工作,quorum不齐全时,wait,默认1分钟,timeout,100,30s, 等待期间,期望活跃的shard数量可以增加,最后实在不行,就会timeout我们其实可以在写操作的时候,加一个timeout参数,比如说put /index/type/id?timeout=30,这个就是说自己去设定quorum不齐全的时候,es的timeout时长,可以缩短,也可以增长。
-
one
要求我们这个写操作,只要有一个primary shard是active活跃可用的,就可以执行。
-
all
要求我们这个写操作,必须所有的primary shard和replica shard都是活跃的,才可以执行这个写操作,主分片和副本都是可用的。