分布式特性及分布式搜索的机制
谈谈Elasticsearch集群的分布式架构
首先说说什么是分布式架构:分布式架构的特点是集群的能力是容易水平扩张的,这个能力包括了数据存储能力,和请求的处理能力,以及保障了整个服务的高可用性。而es集群就具备这样的特点:
* 易扩展:为集群加入新的节点可以提升集群的数据存储能力,和请求处理能力
* 高可用:集群中的某个几点宕机,其他的节点可以替代他的角色。比如说,master节点宕机,master eligible节点会通过选举机制,重新选出一个master节点。一个data节点宕机,因为其他节点存放了宕机节点的数据副本,所以数据的增删改是不受影响的。
其实,需要了解集群架构中不同节点的角色类型以及作用:
节点的本质:java进程,一台机器上可以运行多个es进程,但是生产上建议一台机器运行一个es节点
节点名称 | 作用 |
---|---|
master 节点 | 处理创建,删除索引等请求,决定分片被分配到哪个节点上 ,修改mapping,setting相关配置 |
master eligible 节点 | master节点宕机时可被选举成为主节点,每个es节点默认都是eligible master,集群中的第一个master eligible启动时,它会被选举成为master |
data 节点 | 负责数据存储;执行查询语句并将查询结果返回给coordinating节点,查询过程非常消耗硬件资源,通过扩展数据节点可以水平扩展数据,解决数据单点(性能,故障)问题 |
coordinating 节点 | 提供路由的能力,例如创建所用的请求需要路由到master节点,查询请求需要路由到data节点 ,所有节点默认都是coordinating节点,不可更改 |
Ingest Node | Ingest节点处理时机——在数据被索引之前,通过预定义好的处理管道对数据进行预处理。默认情况下,所有节点都启用Ingest,因此任何节点都可以处理Ingest任务 |
当然我也会很好奇节点之间是如和找到对方进行沟通的?
解决这个问题的方式就是维护集群信息
每个节点都保存了集群信息,但是只有master节点才能修改集群信息,摒弃修改后负责将信息同步给其他节点
这些信息包括:
* 所有的节点信息
* 所有的索引和相关的setting,mapping信息
* 分片路由信息
master eligible节点的选主过程
- 互相 Ping 对⽅方,Node Id 低的会成为被选举的节点
- 其他节点会加⼊入集群,但是不不承担 Master 节点的⻆角⾊色。⼀一旦发现被选中的主节点丢失, 就会选举出新的 Master 节点
es集群的脑裂问题
什么是脑裂问题:集群发生了网络分区后,无master节点的分区选举出了一个新的master节点。当网络分区恢复后,集群中存在两个master节点的现象。
在es7.0版本前,可以通过设置discovery.zen.minimum_master_nodes参数解决,只有集群中的节点数大于这个参数,才能执行选举。比如,当 3 个 master eligible 时,设置 discovery.zen.minimum_master_nodes 为 2,即可避免脑裂。
在es7.0后,不需自己去设置这个参数了,集群可以通过zen2 自动调节所需的最小节点数。
一个数据data Node宕机后,其他节点上的数据副本会重新生成primary分片吗?
master节点单点问题,如果master节点宕机了,集群会怎样?
分⽚片与集群的故障转移
故障转移的大致过程
- 某个节点宕机,节点上的主分片数据P0和副本分片R1数据丢失
- 其他节点上的副本分片升级为主分片
- 重新生成缺失的副本分片,并宠幸分配到节点上
默认的主分片和副本分片数
7.0版本后,默认的主分片数是1,副本分片数是0.
副本分片一定需要和主分片在不同的节点上
⽂文档分布式存储
简单的理解可以是一份索引,它含有很多文档,这些文档分散在不同的分片上,每个分片又分布在不同的机器上。
文档到分片的映射算法
- 轮询,这样的算法使得分片上节点的数据量十分均匀,但有可能导致查询时访问的分片数过多
- 文档到分片的路由,可以指定路由值,路由值相同的数据路由到不相同的分片上,查询同一路由值的数据时,查询一个分片就可以了。算法:shard = hash(_routing) % number_of_primary_shards
- 创建索引后,primary的数量是不可以随意改动的,因为如果使用的映射算法是路由的话,primary_shard的数量变化,导致路由算法变化,新增时算得的分片值和查询时算得的路由值不一样,最终导致查询不到数据。如果要改primary的数据,需要reindex才行。
分⽚片及其⽣生命周期
es的中的分片和lucene中的index是什么关系
- es中的shard就是lucene中的index
- lucene中的index其实是由多个segment组成的
- segment是什么,segment里面存放的是文档数据。当执行refresh操作时,index buffer中的idnex document会被写入到lucene的segment中。
es中的刷新操作指令汇总
- refresh:将index buffer写入segment的过程。默认一秒执行一次,可以通过index.refresh_interval参数修改频率。refresh后,数据就可以被搜索到了。index buffer被占满时,也会触发refre操作,默认是JVM的10%;调用refresh后数据会被清空。
- flush:这是个非常长的操作过程,包含这下面几个步骤:
* 调用refresh:数据写入segment,index buffer清空
* 调用fsync,将segment中的数据写入磁盘
* 清空transaction Log
* 默认30分钟调用一次
* Transaction Log 满 (默认 512 MB),也会调用 - merge:
作用:将多个segment文件合并成一个,较少segment的数量;删除.del文件(删除已经删除的文档)
触发:es和lucene会自动进行merge操作;也有支持手动merge的API
POST my_index/_forcemerge
什么是commit point
- commit point:记录了所有的segments信息
- .del: 删除的文档信息保存在.del文件中
什么是transaction log?
transaction log 的作用就是为了防止,文档在未落盘前,es机器宕机导致数据丢失的情况。
数据在写入index buffer的同时,数据会被写入到transacion log。
剖析分布式查询及相关性算分
分布式搜索的运行机制
分成两个阶段:第一阶段query,第二阶段fetch
query阶段
用户发出query请求 -> 收到请求的节点以coordinating的身份将请求转发data Node -> 被选中的分片执行查询,进行排序。然后每个分片都会返回from+size个排序后的文档id和排序值给coordinating节点。
fetch阶段
coordinating node 节点会将query阶段从每个分片获取到的文档id进行重新排序,获取从from到from+size个文档id。以muiti get请求的方式,到相应的分片获取详细的文档数据。
潜在问题
- 深度分页问题:深度分页时,协调节点需要处理的数据会比较多,影响查询性能。
- 相关性算分问题:文档的打分在分片之间是相互独立的,不同的分片的数据量不一致打分就会不太准确,特别是数据量少的时候。解决方案:一是数据量少的时候,索引设置成一个分片即可。二是使⽤DFS Query Then Fetch(到每个分⽚片把各分⽚片的词频和⽂文档频率进⾏行行搜集,然后完整的进⾏行行⼀一次相关性算分, 耗费更更加多的 CPU 和内存,执⾏行行性能低下,⼀一般不不建议使⽤用)
谈谈es中的排序场景
实战
## sort关键字是和query一个层级的,没有指定_score排序,所以不会计算评分
POST kibana_sample_data_ecommerce/_search
{
"size": 20,
"sort": [
{
"order_date": {
"order": "desc"
}
}
],
"query": {
"match_all": {}
}
}
## 可以通过指定score,使得在评分相同的时候按照评分进行排序
POST kibana_sample_data_ecommerce/_search
{
"size": 20,
"sort": [
{
"order_date": {
"order": "desc"
}
},
{"_score":{"order": "desc"}}
],
"query": {
"match_all": {}
}
}
## 如果对text类型的字段进行排序,会报错。因为排序会使用fielddata,而text类型的fielddata字段默认是disabled。需要手动改变索引的mapping, 将fielddata设置为true,才能正常使用。fielddata只是用于text类型的。
POST kibana_sample_data_ecommerce/_search
{
"size": 20,
"sort": [
{
"customer_full_name": {
"order": "desc"
}
},
{"_score":{"order": "desc"}}
],
"query": {
"match_all": {}
}
}
PUT kibana_sample_data_ecommerce/_mapping
{
"properties": {
"customer_full_name" : {
"type" : "text",
"fielddata": true,
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
排序过程
* 针对原始字段进行,倒排序无法发挥作用
* 需要使用正排索引,通过文档id和字段快速获取原始内容
* 实现方式有2种,fielddata -> text, doc values-> 列式存储,非text类型字段
doc value 对比 fielddata
fielddata应用介绍
- 默认关闭,可以通过 Mapping 设置打开。修改 设置后,即时⽣生效,⽆无需重建索引
- 其他字段类型不不⽀支持,只⽀支持对 Text 进⾏行行设定
- 打开后,可以对 Text 字段进⾏行行排序。但是是对 分词后的 term 排序,所以,结果往往⽆无法满⾜足 预期,不不建议使⽤用
- 部分情况下打开,满⾜足⼀一些聚合分析的特定需求
- 参考文章:https://www.elastic.co/guide/cn/elasticsearch/guide/current/_limiting_memory_usage.html
Doc Values 应用介绍
- 默认启⽤用,可以通过 Mapping 设置关闭
- 关闭后,增加索引的速度 / 减少磁盘空间
- 如果重新打开/关闭,需要重建索引
- 什么时候需要关闭:明确不不需要做排序及聚合分析
- 可以正常支持搜索和结果字段展示
使用docvalue_fields, 可以查询text字段分词后的结果
PUT test_keyword
PUT test_keyword/_mapping
{
"properties": {
"user_name":{
"type": "text",
"fielddata":true
}
}
}
PUT test_keyword/_doc/1
{
"user_name":"郎思明"
}
POST test_keyword/_search
{
"docvalue_fields": [
"user_name"
]
, "query": {
"match": {
"user_name": "林零"
}
}
}
es对于并发问题的处理是如何的
es是使用乐观锁进行并发控制的。
es中的文档是不可变更的。你更新一个文档,es会将原文档标记为删除,然后增加一个全新的文档,并且version字段增加1。
内部版本控制:if_seq_no + if_primary_term
外部版本:version + version_type(external)
什么是内部版本是是外部版本?这两种控制版本并发的方式有什么区别?
6.7版本以后,不允许使用内部版本的并发控制,就是指使用version的这种写法。
primary_term和seq_no的说明
对于if_primary_term记录的就是具体的哪个主分片,而if_seq_no这个参数起的作用和旧版本中的_version是一样的,之所以加上if_primary_term这个参数主要是提高并发的性能以及更自然,因为每个document都只会在某一个主分片中,所以由所在主分片分配序列号比由之前通过一个参数_version,相当于由整个ES集群分配版本号要来的更好。
https://www.elastic.co/guide/en/elasticsearch/reference/6.7/optimistic-concurrency-control.html
version_type的含义
通过自己设置一个外部值来判断当前记录的新旧状态,比如你想要version<=5的才进行更新,否则返回失败
?version=5&version_type=external
version_type=external代表你要用外部的值进行判断
version=你的判断值
segment merge的过程是怎样的呢?
考虑点:
- 尽可能合并较小的segment,足够大的segment可以不参与合并 -》需要确定可以参与合并的segment的最大长度
- 每次合并的segment的数量需要限制,不能一次合并太多的segment -> 需要确定一次合并最大可以处理的segment的数量
- 大小相近的segment应该被分到一组进行合并,具体是出于什么原因考虑,目前还不知。
参考文章:https://zhuanlan.zhihu.com/p/65075215
杂文
深入理解 Lucene 的 flush 过程
https://mp.weixin.qq.com/s/wI3E2ZLncHNCd_-T3tBFPw
扒一扒查询缓存的裤子
https://mp.weixin.qq.com/s/q5iN2rOwkb0Bw1MXowZ6LQ
Elasticsearch 中增加分片数量,聚合一定会变快吗?
https://mp.weixin.qq.com/s/MJlgoQFFlfvUFygwZyJuAQ