从源码角度了解Elasticsaerch(分布式协调排序、深分页问题)

引文

Elasticsearch基于Lucene所以很多系统实现都在其中,所以可以先看看Lucene的实现:
https://blog.csdn.net/qq_35040959/article/details/147931034

项目组件

不像Kafka这种顶级项目核心性能组件全自己实现,ELK中有很多引用至第三方开放库;

网络模型-Netty

网络模型多重要不必多说,Elasticsearch基于Netty,一个认可度&社区活跃度都非常高的NIO多路复用模型;

存储模型-Lucene

Elasticsearch基于Lucene上提供分布式服务;具体可以看看这篇文章对存储模型的介绍
https://blog.csdn.net/qq_35040959/article/details/147931034

分布式查询

//GET http://localhost:9200/my-index-000001/_search
{
  "from": 10, //第二页
  "size": 10, //查询十条
  "_source": ["title", "priority"], //指定字段返回
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "guide" } }//全匹配
      ]
    }
  }
}

构建查询
org.elasticsearch.action.search.TransportSearchAction#executeSearch

分发到每个shard
org.elasticsearch.action.search.AbstractSearchAsyncAction#run

发送shard请求
org.elasticsearch.transport.TransportService#sendLocalRequest

单条查询(查询Lucene)
org.elasticsearch.search.query.QueryPhase#searchWithCollector

[Query]阶段查询结果,只包含指定范围的score与docid
org.elasticsearch.action.search.QueryPhaseResultConsumer#consumeResult

聚合数据(对多个shard的数据聚合排序后决定实际获取的)
org.elasticsearch.action.search.FetchSearchPhase#innerRun -> resultConsumer.reduce();

[Fetch]阶段,读取指定docid文档
org.elasticsearch.action.search.FetchSearchPhase#executeFetch

[Query]&[Fetch]步骤基于Lucene的相关性查询与指定docid查询

深分页-search_after分页查询

**<<分布式查询>>**是基于相关性的查询,这种查询会扫描倒排表构建K-TOP堆,这种方法在深页后(如10000页后)查询成本非常高;

search_after必须指定"sort"排序字段后通过"search_after"定位在排序中的其实位置,这种方式没有构建K-TOP的流程,定位到指定的排序偏移量后直接获取,更加快速;
缺点就是不再是相关性排序;

//GET http://localhost:9200/my-index-000001/_search
{
  "from": 0,   
  "size": 10,     
  "_source": ["title", "priority"], 
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "guide" } }
      ]
    }
  },
  "sort": [
    { "_id": "asc" } //强制要求:指定排序(不难理解:没有固定的排序规则,那么给定的search_after将没法用于排查此前已经吐出的数据)
  ],
  "search_after": [ "QHHnZ5cBTxKf9WaM2zpf"]  //强制要求:上一页最后一条文档_id
}

基于DocValues实现

深分页-scroll查询

search_after不是基于快照,会受到同步数据插入影响,scroll查询对索引打快照;

资源占用与性能对比:

特性 Scroll API Search After API
上下文维护 需要维护 _scroll_id 上下文 无上下文,仅需传递排序值
内存占用 高(需缓存快照和游标状态) 低(无持久化状态)
CPU 开销 低(顺序扫描,无需动态排序) 中等(需动态定位排序值位置)
网络传输 批量高效(适合大数据量导出) 单页高效(适合高频分页请求)

实时性与一致性对比

特性 Scroll API Search After API
数据一致性 强一致性(基于快照,数据静态) 弱一致性(实时数据,可能变化)
反映更新 否(快照期间索引更新不可见) 是(每次查询读取最新数据)
适用场景 全量导出、离线分析(如日志备份) 实时分页、深度检索(如用户搜索)

todo 快照原理

一致性协议

分布式问题

主节点

Elastic有一下节点角色[Master Node(主节点)]、[Data Node(数据节点)]、[Coordinating Node(协调节点)]三个核心角色(还有其他的角色),主要介绍主节点:

​​主节点(Master Node)​​

  • ​​职责​​:管理集群元数据(索引创建/删除、分片分配、节点状态监控)。

  • 细分类型​​:
    – 候选主节点(Master-Eligible)​​:参与选举,可成为主节点(配置:node.roles: [master])。
    – 仅投票主节点(Voting-Only)​​:仅参与选举投票,不担任主节点(配置:node.roles: [master, voting_only])。

  • ​​最佳实践​​:
    – 生产集群需至少 ​​3个候选主节点​​,且半数以上非仅投票节点,避免脑裂。
    – 与数据节点分离,确保稳定性。

集群中将会只有一个节点成为[Master Node]并为集群中需要强一致性的行为做唯一决策出口

主节点选举

Elasticsearch 的选举机制是对 Raft (论文)的​​工程化改良​​,通过牺牲部分理论严谨性换取分布式搜索场景下的实操效率,核心差异本质是​​性能与强一致性之间的权衡​​

1. 多票制​​

​​节点在同一任期内可多次投票,支持多个候选人同时当选。
​​冲突解决​​:若多个候选人同时当选,ES 采用 ​​“最后当选者有效”​​ 原则(如 Node2 先当选,但收到 Node3 的投票请求后主动退位,最终 Node3 成为主节点)。
​​目的​​:避免小规模集群中因节点同时竞选导致选举失败(如 3 节点均自投),提升选举速度

2. 预投票流程(PreVote)的差异​​

​​Raft :
候选人需确认日志足够新(term 更大或 index 更大)且获得多数支持,才发起正式投票。
​​Elasticsearch 的 PreVote​​:
检查条件更宽松:仅需候选人 term ≥ 当前节点 term,或 term 相同时候选人版本号 ≥ 当前节点版本号。
​​风险​​:宽松条件可能导致网络分区节点频繁发起无效选举,但提高选举速度,同时 ES 通过动态退避机制(back_off_time)降低影响。

3. 状态转换规则的灵活性​​

​​Raft 的严格转换​​:
节点状态需按 Follower → Candidate → Leader 顺序转换,且 Leader 退位后需先回退至 Follower。
​​Elasticsearch 的灵活转换​​:
​​Leader 退位规则​​:Leader 收到任何更高 term 的投票请求(RequestVote)时,​​立即退位为 Candidate​​(而非 Follower)。
​​目的​​:加速新主节点产生,避免旧 Leader 阻塞选举进程

4. 脑裂处理策略​​

​​Raft 的严格多数票​​:
仅获得多数票的分区可选出 Leader,天然避免脑裂。
​​Elasticsearch 的妥协方案​​:
允许多个分区同时选出 Leader,但通过 ​​“最后当选者有效”​​ 和 ​​Leader 主动退位​​ 解决冲突(非实时避免脑裂)。
​​风险​​:极端情况下可能短暂存在多主,但数据一致性通过分片分配机制(主节点唯一管理元数据)保障。

5. 性能与规模适应性​​

​​选举速度​​:
ES 的并行投票和宽松 PreVote 使​​小规模集群选举更快​​(通常 < 500ms)。
Raft 的严格流程在大规模集群中​​稳定性更优​​,ES 需依赖参数调优(如增大 cluster.election.duration)减少竞争。
​​大规模集群挑战​​:
ES 在超 40 节点时可能出现​​频繁主节点切换​​,Raft 可稳定支持 50+ 节点。

从源码角度了解Elasticsaerch(分布式协调排序、深分页问题)_第1张图片

cluster.election.initial_timeout: 100ms   # 初始等待时间
cluster.election.back_off_time: 100ms     # 退避增量
cluster.election.max_timeout: 10s          # 最大等待时间

仅投票节点(Voting-only Node)​​:专用于投票但不存储数据,提升大规模集群选举稳定性。

​​小规模集群​​(<10 节点):ES 默认配置即可,利用多票制提升选举效率。
​​大规模集群​​(>20 节点):
部署专用​​主节点​​(无 data 角色)和​​仅投票节点​​;
调大 cluster.election.duration(如 1s)减少竞争。

添加新分片&重平衡的影响

一、修改副本分片数(Replicas)​​
​​场景​​:增加或减少副本分片数(如从number_of_replicas=1改为2)。
​​特点​​:在线操作,无需重建索引,数据不会丢失。

添加副本可以在线操作:看过raft论文的添加新节点的流程就能理解为什么可以在线操作;

二、修改主分片数(Primary Shards)​​
​​场景​​:调整主分片数量(如从number_of_shards=3改为6)。
​​特点​​:必须重建索引!原始索引数据不会自动迁移到新分片布局。

ES数据使用计算Hash分配数据,修改只能重建索引;

写一致性

你可能感兴趣的:(从源码角度了解Elasticsaerch(分布式协调排序、深分页问题))