Elasticsearch基础知识补齐
前面的一些学习笔记都是简单记录了一下 CURD
和 Mapping
, 这篇博文就记录一下 Es
的一些基础知识
分片的管理
主副分片
文档的数据是存储到索引中的,而索引的数据是存储在分片上的,而分片是位于节点上的。
分片 shard
有主分片 primary shard
和复制分片 replica shard
两种,其中主分片是主要存储的分片,可以进行读写操作;副本分片是主分片的备份,可以进行读操作不支持写操作。
查询可以在主分片或副本分片上进行查询,这样可以提供查询效率。但数据的修改只发生在主分片上
分片是面向索引的,分片上的数据属于同一个索引,在我们创建索引的时候,可以指定主分片和副本分片的数量,默认是5个主分片,5个副本分片。
索引的replica shard
的数量可以修改,但primary shard
的数量不可以修改。一个Primary Shard
可以有多个Replica Shard
,默认创建是1个。
为了保证数据的不丢失,通常来说Replica Shard
不能与其对应的Primary Shard
处于同一个节点中。因为万一这个节点损坏了,那么存储在这个节点上的原数据(primary shard)和备份数据(replica shard)就全部丢失了,但是可以和其他primary shard
的replica shard
放在一起
replica shard
是primary shard
的副本分片,负责承载一定的读请求,当primary shard
都挂掉后,其中一个replica shard
会变成primary shard
来维持写功能。
分片的均衡分配
分片是分配到节点上的,但它会默认地均衡分配。
所谓均衡分配,以情况举例:priX代表主分片,repX代表副本分片
- 索引有1个
priA
,1个repA
,但当前只有一个节点A
,那么priA
分配到A节点
;repA
没有分配。优先分配主分片。 - 索引有1个
priA
,1个repA
,当前有两个节点A和B
,那么priA
分配到A节点
;repA分配到B节点
(priA
分配到B节点也有可能)。 - 索引当前有2个
priA
,4个repA
,有3个节点,那么现在每台节点上有两个分片(但要考虑主分片不能与自己的副本分片同在一个节点上,在下面的主副分片的排斥中由一个例子)。
当后续新增节点的时候,会自动再次重新均衡分配。
主副分片的排斥
考虑到备份的安全性,不应该让主分片和副本分片位于一个节点上,不然可能完全丢失数据。(比如你为了避免丢失钥匙,你给你家的门准备两条钥匙,你有一个钱包和一个背包,结果你把两条钥匙都放到钱包中,某天你钱包丢了,于是你回不了家了。所以你应该把钥匙分开放。)
对应的排斥情况就是:
- 索引有1个
priA
,1个repA
,但当前只有一个节点A,那么priA
分配到A节点;repA
没有分配。 - 索引有1个
priA
,1个repA
,当前有两个节点A和B,那么priA
分配到A节点;repA
分配到B节点(priA
分配到B节点也有可能)。
请注意,主分片与副本分片不能在一起,但副本分片和副本分片能存放在一起
- 下面再举个例子
你有一个索引,索引有2个主分片,4个副本分片每个主分片对应两个副本分片,3个节点
主分片1名Pir1
,他的两个副本分片是 Rep1
,Rep2
主分片2名Pir2
,他的两个副本分片是 Rep3
,Rep4
那么综合考虑了均衡分配和主副分片的互斥之后,可能有以下结果
容错性
节点是有可能宕机的,宕机后,那么这个节点的数据起码会暂时性的丢失,那么对于不同情况下,最多可以宕机多少个节点呢?下面举例:pri = x代表主分片数量为X,rep是副本分片
- 如果你只有一个节点,那么容错性为0,你不能宕机,宕机不完全意味着数据完全丢失,但暂停服务还是有的。
- 如果你有两个节点,pri = 2,rep = 2,那么此时分片分配应该是
[P1R2, P2R1]
,此时容错性为一个,因为某个节点上有完整的两个分片的数据 此处一提,假设丢失了P2R1,那么R1R2中的R2会升级成primary shard来保持写功能 - 我们可以综合均衡分片和排斥性来考虑我们需要的节点数、主分片和副本分片数量。
数据路由
每一个 document
会存储到唯一的一个 primary shared
和其副本分片 replica shard
中,我们是怎么根据 ID
来知道这个 document
存储在哪个分片的呢?
ElasticSearch
会自动根据 document
的 id
值来进行计算,最终得出一个小于分片数量的数值,这个数值就是分片在 ElasticSearch
中的序号,最终得出应该存储到哪个分片上。
类似原理举例,假设我现在有4个主分片,那么我给它们标序号0,1,2,3。现在进来一个ID为15的数据,我经过一系列计算之后,15入参之后假设得到243这个数值,然后我们使用243来对4求余,余数是3,所以就把这个数据放在序号为3的分片上。
依据这个原理,存储数据的时候就知道把数据放在哪个分片上;读取数据的时候也知道从哪个分片上读取数据。
稍微提一下,在 ElasticSearch
全文搜索完成之后,此时内部得到的是 ID
组成的数组,内部再会根据 ID
来查找数据
集群
首先,每一个 ElasticSearch
服务端就是一个集群节点,当我们启动一个 elasticsearch
时,就相当于启动了一个集群节点。
那么,多个节点之间如何建立联系呢?发现机制
当我们启动一个节点的时候,这个节点会自动创建一个集群,集群的名称默认为 elasticsearch
(也可以在config/elasticsearch.yml配置cluster.name),当我们再次启动一个节点的时候,它首先会尝试寻找名称为 elasticsearch
的集群,然后加入其中,(所有的都是先尝试寻找,没有再自己创建),当节点加入到集群中后,这个集群就算是建立起来了。
健康状态
当集群建立后,我们可以使用 API
来查看集群状态
返回结果解析:
epoch
时间戳timestamp
时间cluster
集群名status
状态集群状态解析看下面。node.total
集群中的节点数node.data
集群中的数据节点数shards
集群中总的分片数量pri
主分片数量relo
副本分片数量init
初始化中的分片数unassign
没有被分配的分片的数量pending_tasks
待处理的任务数max_task_wait_time
最大任务等待时间activeds_percent
active的分片数量
集群状态解析 yellow\red\green
集群状态受当前的 primary shared
和 replica shared
的数量影响。
当每个索引的 primary shared
和 replica shared
都是 active
的时候,状态为 green
;什么是 active
? shard
是位于节点上的,一个 shard
被分配到了运行的节点上,那么此时就是 active
的,如果 shard
没有分配到节点上,那么就是 inactive
当每个索引的 primary shared
都是 active
的,但 replica shared
不完全是 active
的时候,状态为 yellow
;
当每个索引的 primary shared
不完全是 active
的时候,此时发生了数据丢失,状态为 red
.
- 举个例子
当前节点中只有一个索引A,索引A当前有2个priA
,4个repA
,那么此时需要两个节点才可以变green
,
一个的时候就不说了,此时所有副本分片都inactive
;两个节点的时候,由于每个主分片有两个副本分片,此时的分配是一个主分片+两个副本分片注意互斥性
为什么现在是yellow?
当我们第一次启动的时候,因为我启动了 kibana
, kibana
会默认为我们创建一个名为 kibana
的索引,这个索引的 primary shard
和 replica shard
都为1,由于此时只有一个节点,所以会优先分配 primary shard
,而忽略replica shard
(不要忘了基于备份的安全性的 shard
排斥考虑:主分片和副本分片不能位于同一个节点上),所以此时就符合了每个索引的primary shared
都是active
的,但replica shared
不全是active
的,所以此时是 yellow
.
你可以尝试启动多一个elasticsearch
节点来调整状态。
请求的处理
提供服务
节点是提供服务的单位,请求是发到节点上的。节点接收到请求后,会对请求进行处理:
- 读请求:
当接受读请求,如果是根据ID来获取数据的读请求,那么它会选择有这个ID
的数据的分片来获取数据(可以根据ID
来判断该分片上是否有这份数据);如果是搜索类的请求,首先会根据索引表来搜索,得出相关文档的ID
,然后根据ID
从这个索引的相关分片来获取数据(如果有2个pri
,2个rep
,那么搜索的分片可能是p1r2
、p2r1
、p1p2
,r1r2
,只要能完整地获取索引的所有数据即可)。
- 写请求:
当接收写请求,节点会选择根据ID
来计算应该把这个数据存储到哪个主分片上,然后通过主分片来修改数据,主分片修改完成后,会将数据同步到副本分片上。由于存在一个同步过程,同步完成之前,某个分片上可能不存在刚刚插入的数据,但概率较小,因为同步是极快的(NRT)
再次提醒,主分片有读和写的能力,副本分片只可以读,所以数据的更新都发生在主分片上
协调节点cordination node
索引的数据是存储在节点上的,当一个请求发到节点上的时候,可能这个节点上并没有这个索引的数据,那么这个时候就需要把请求转发给另一个节点了,这时候原本的节点就是一个协调节点。
请求分发的负载均衡
一个索引存储在多个主分片和副本分片上,索引的数据会平均分配到每一个主分片中,然后每一个副本分片拷贝对应主分片的数据。
对于数据存储,数据是存储在分片上的,而分片位于节点上,在上面说了分片会均衡分配到每一个节点上,这样也保证了节点上的数据量是平均的。
除此之外,对于读取某一个主分片及其副本分片上的数据的时候,会使用轮询算法来将读请求平均分配(大概意思就是,假设现在对于主分片1有三个副本分片,那么总数为4,假设分别编号1、2、3、4,那么可能地,第一次请求交给了1,那么第二次请求要交给2,第三次要交给3。。。以此类推,超过4则从头开始)。
文档的数据类型
ElasticSearch
主要有以下这几种数据类型:
- 字符类:
text:是存储字符串的类型,在 elasticsearch 中存储会分词的字符串数据一般用text keyword:也是存储字符串的类型,在elasticsearch中用于存储不会分词的、结构化的字符串数据
- 整数类型:
integer
、long
、short
、byte
- 浮点数类型:
double
、float
- 日期类型:
date
- 布尔类型:
boolean
- 数组类型:
array
- 对象类型:
object
除了上述的类型之外,还有一些例如half_float
、scaled_float
、binary
、ip
等等类型,由于不是非常基础的内容,所以这里不讲,有兴趣的可以自查。
keyword
- 我们定义
mapping
的时候有定义一个keyword
,这是什么?有什么用?
ElasticSearch
对于一些类型的字段,例如text
类型的字段,默认是会进行分词的。但如果我们并不想分词呢?一些数据我们想要非常精确地查找,并且只找到我们搜索的数据的时候,这个字段是不应该分词的。那么我们可以使用keyword
来存储完整的原有的数据,keyword
会作为一个索引词,然后我们针对字段.keyword
来搜索。
理论上,这个不分词的字段的数据应该是比较简短的,太长的话可能就没有必要不分词了。所以在定义keyword
的时候还可以提供"ignore_above": 数值
来限制超过多少个字符就不使用原有数据创建keyword
现在版本中,对于默认分词的字段,现在会默认附加一个keyword
。
keyword
适用于不分词的搜索的情况,在keyword
中的数据不会分词。