Elasticsearch - Cluster Intro PART1
分布式集群介绍
Node
本子上是一个JVM进程
-
Coordinating Node
- 处理请求的节点,叫 Coordinating Node
- 所有节点默认都是 Coordinating Node
- 通过将其他类型设置成 False,使其成为 Dedicated Coordinating Node
-
Data Node
- 节点启动后,默认就是数据节点。可以设置 node.data: false 禁⽌
- 保存shard数据。(由 Master Node 决定如何把shard分发到数据节点上)
- 增加数据节点,水平扩展
-
Master Node
-
决定shard片被分配到哪个节点 / 负责索引的创建与删除
-
维护cluster state
-
防止单点
-
Master Eligible Nodes
- ⽀持配置多个 Master Eligible 节点。这些节点可以在必要时(如 Master 节点出现故障,网络故障时)参与选主流程,成为 Master 节点
- 当集群内第一个 Master eligible 节点启动时候,它会将⾃⼰选举成 Master 节点(preVoted)
- 每个节点启动后,默认就是一个 Master eligible 节点
-
Cluster State
- 所有的节点信息维护
- shard的路由信息
- 索引的mapping & setting信息等
- 每个Node都保存了状态信息
- 只有master(leader)才能修改集群信息,并广播同步
Coordinating - Leader Election
-
gossip like Nodes互ping,preVote & Vote 后,node id 低的成为被选举节点 - bully 算法
-
非master node加入集群,不影响选举。leader master故障后重新选举
-
Split brain 问题
- quorum-based 算法,只有在 Master eligible 节点数大于 quorum 时,才能进行选举
- Quorum = (master 节点总数 /2) + 1 时是安全的
提高cluster可用性,需要Data/Coordinating/Master 多节点,减少故障带来的影响
Data容错和故障恢复
Primary and Replica Shard
除了Primary Shard,增加replica shard可以增加数据冗余,提高可用性
-
Primary Shard,可以将一份索引的数据,分散在多个 Data Node 上,实现存储的⽔平扩展
- 过多的primary,导致数据聚合成本较高,影响性能
- 过少的primary shard,如1个,缺少水平扩展能力
- 需要根据业务场景balance
-
Primary Shard数在索引创建时候指定,后续默认不能修改,如要修改,需重建索引
-
一旦主分片丢失,副本分片可以 Promote 成主分片。副本分片数可以动态调整
- 过多的replica ,带来更多的写入负担,数据持久化安全性提升,需要balance
-
Replica shard 提升读取的吞吐量
文档路由
⽂档到分片的映射算法
-
随机 / Round Robin。当查询⽂档 1,分片数很多,需要多次查询才可能查到 ⽂档 1
-
维护文档到分片的映射关系,当文档数据量大的时候,维护成本⾼
-
实时计算,通过文档 1,⾃动算出,需要去那个分片上获取⽂档
-
余数算法
- shard = hash(_routing) % number_of_primary_shards
-
支持自定义routing规则
- 默认doc_id
文档更新
流程
-
primary updated-> replica updated -> response
-
Index Immutable
- Append Only 并发写减少lock使用
- 容易被cached和压缩
- 更新挑战,需要重建index
-
并发写入update
- ES 中的⽂档是不可变的。如果你更新⼀个文档,会将就文档标记为删除,同时增加一个全新的文档。同时将文档的 version 字段加 1
- seq_no + primary_term
-
Lucene 中,单个倒排索引⽂件被称为Segment
- Segment 是⾃包含的, 不可变更的。多个 Segments 汇总在一起,称为 Lucene 的Index,其对应的就是 ES 中的 Shard
- 当有新文档写⼊时,会⽣成新 Segment,查询时会同时查询所有 Segments,并且对结果汇总
- Lucene 中有一个文件,用来记录所有Segments 信息,叫做 Commit Point
- 删除的文档,保存在.del文件中
-
Refresh 操作
- 将 Index buffer 写入 Segment 的过程叫Refresh。 Refresh 不执行 fsync 操作,不保证持久化道磁盘
- Refresh 频率:默认 1 秒发⽣生一次,可通过index.refresh_interval 配置。 Refresh 后,数据就可以被搜索到了
- 短时间大量写入会有大量的segment
- Index buffer 被占满时,会触发refresh
- 可以定时或手动触发refresh
-
Flush 操作
-
先执行refresh操作
-
再调⽤ fsync,将缓存中的 Segments写入磁盘
-
最后删除Transaction Log
-
触发
- Time-based 30 分钟
- Size-based trans log 512MB
- manual
-
-
Merge 操作
-
减少 Segments / 删除已经删除的⽂档
-
触发
- 自动merge
- 手动api
-
-
为什么增加Trans log
- Segment 写⼊磁盘的过程相对耗时,借助⽂件系统缓存, Refresh 时,先将segment 写⼊缓存以开放查询,提升性能
- 未保证数据不丢,增加commit log - trans log
- refresh 不影响tran slog
- 故障恢复,即使segment丢失,translog sync 持久化,可以保证数据恢复
- trans log结构比segment简单,写入性能有保证
搜索查询机制
Query
从shards 检索出数据,根据score进行排序,获得文档id+score list
-
coordinating node 向primary shard 发送请求
-
被选中的shard执⾏查询, 进⾏排序。然后,每 个分⽚都会返回 From + Size 个排序后的文 档 Id 和排序值 给 Coordinating 节点
Fetch
通过👆的文档ids fetch获取文档data
- Coordinating Node 会将 Query 阶段,从每个shard获取的排序后的⽂档 Id 列表,重新进⾏排序(可编程排序函数),选取 From 到 From + Size 个⽂档的 Id
- 以 multi get 请求的⽅式,到相应的shard获取详细的⽂档数据
性能问题
- 每个shard都查from+size条数据
- 每个shard 单独算分,文档少分片多的情况下,算分不准
- 合并成本
- 深度分页查询
如何解决?
-
数据量不大的时候,可以将主分⽚数设置为 1
-
数据量大时,保证文档均匀分布在shard
-
使用DFS Query then fetch
- 性能差,需要注意
-
深度分页,使用Search After
- latest doc id
结果排序
Text 类型排序
-
sort 指定排序是针对字段原始内容进行的。 倒排索引⽆法发挥作⽤
-
需要⽤到正排索引。通过文档 Id 和字段快速得到字段原始内容
- Fielddata,可利用cache,数据量大时,内存开销大,默认关闭
- Doc Values(列存储,对text类型无效) ,利用磁盘文件
Bucket & Metric 聚合分析
-
bucket 满足条件的文档
-
term
-
数字类型
- range
- histogram
-
-
Metric 聚合分析
- 单值分析 min max avg sum cardinality
- 多值分析 stats top hits percentile
POST employees/_search
{
"size": 0,
"aggs": {
"Job_gender_stats": {
"terms": {
"field": "job.keyword"
},
"aggs": {
"gender_stats": {
"terms": {
"field": "gender"
},
"aggs": {
"salary_stats": {
"stats": {
"field": "salary"
}
}
}
}
}
}
}
}
👆代码 表示 根据工作类型分桶,然后按照性别分桶,计算工资的统计信息
数据建模
- 确认字段类型
- 是否在搜索范围及分词
- 是否需要聚合和排序
- 是否需要额外的存储
设置多字段类型
-
默认会为文本类型设置成 text,并且设置一个 keyword 的⼦字段
-
在处理理人类语⾔言时,通过增加“英文”,“拼音”和“标准”分词器,提高搜索结构
-
结构化数据
- 贴近自身数据类型 日期 布尔
- 枚举=>keyword
- 数字能用byte 不使用long
-
不需要搜索的字段,显示设置index = false,如url
-
不需要聚合的字段,显示设置enable = false
-
避免过多的字段,仅索引需要检索的内容
-
避免正则查询 😄这个大多数场景都需要避免
- filter过滤替代
-
避免null
- 使用null_value
参考
深入理解Elasticsearch PDF