← 返回文章列表

Elasticsearch 底层原理系列(八):分布式一致性

2020-12-20·5 分钟阅读

前言

分布式系统的一致性是一个复杂而关键的问题。Elasticsearch 作为分布式搜索引擎,需要处理节点故障、网络分区等异常情况,同时保证数据的可用性和一致性。

本章将深入解析 ES 的分布式一致性机制。

技术亮点

技术点难度面试价值本文覆盖
选举机制⭐⭐⭐⭐高频考点
脑裂问题⭐⭐⭐⭐高频考点
故障检测⭐⭐⭐实战价值
数据一致性⭐⭐⭐⭐⭐进阶考点

面试考点

  1. Elasticsearch 如何保证分布式一致性?
  2. 什么是脑裂?如何避免?
  3. ES 的故障检测机制是怎样的?
  4. 节点崩溃后数据如何恢复?

选举机制详解

ES 7.x+ 选举算法

┌─────────────────────────────────────────────────────────────────────────┐
│                    ES 选举算法演进                                        │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ES 7.0 之前:Zen Discovery(基于 Bully 算法)                           │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  问题:                                                         │   │
│  │  • 需要手动配置 minimum_master_nodes                            │   │
│  │  • 网络抖动可能导致频繁选举                                     │   │
│  │  • 脑裂风险                                                     │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  ES 7.0+:改进的选举算法(类似 Raft)                                    │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  改进:                                                         │   │
│  │  • 自动计算 minimum_master_nodes = N/2 + 1                      │   │
│  │  • 单节点也能正常启动                                           │   │
│  │  • 减少选举冲突                                                 │   │
│  │  • 更快的收敛速度                                               │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

选举流程

┌─────────────────────────────────────────────────────────────────────────┐
│                        选举流程详解                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  状态机:                                                                │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  ┌──────────────┐                                              │   │
│  │  │    START     │ ─── 启动                                     │   │
│  │  └──────┬───────┘                                              │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │  ┌──────────────┐                                              │   │
│  │  │  DISCOVERING │ ─── 发现集群                                 │   │
│  │  └──────┬───────┘                                              │   │
│  │         │                                                       │   │
│  │         ├─── 发现 Master ────────► FOLLOWER                    │   │
│  │         │                                                       │   │
│  │         └─── 未发现 Master                                      │   │
│  │                 │                                               │   │
│  │                 ▼                                               │   │
│  │         ┌──────────────┐                                       │   │
│  │         │ CANDIDATE    │ ─── 参与选举                          │   │
│  │         └──────┬───────┘                                       │   │
│  │                │                                                │   │
│  │                ├─── 获得多数票 ──► MASTER                       │   │
│  │                │                                                │   │
│  │                └─── 未获多数 ──► 继续选举                        │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  选举条件:                                                              │
│  • 没有 Master                                                          │
│  • 当前节点是 Master-eligible                                           │
│  • 已发现足够的节点(满足 minimum_master_nodes)                         │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

投票排序规则

┌─────────────────────────────────────────────────────────────────────────┐
│                    投票排序规则                                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  节点排序依据:                                                          │
│  1. 集群状态版本(越高越优先)                                          │
│  2. 节点 ID(字典序)                                                   │
│                                                                         │
│  示例:                                                                  │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  Node A: version=10, id=node_abc                               │   │
│  │  Node B: version=10, id=node_def                               │   │
│  │  Node C: version=9,  id=node_ghi                               │   │
│  │                                                                 │   │
│  │  排序结果:Node A > Node B > Node C                             │   │
│  │  (版本相同,按 ID 排序)                                        │   │
│  │                                                                 │   │
│  │  投票结果:Node A 获得 3 票(全票)当选                          │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  排序优先级的设计目的:                                                  │
│  • 版本高 = 拥有最新数据                                                │
│  • 避免旧数据的节点当选                                                 │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

脑裂问题

脑裂场景

┌─────────────────────────────────────────────────────────────────────────┐
│                        脑裂场景                                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  正常状态:                                                              │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐                      │   │
│  │  │ M1  │ │ M2  │ │ M3  │ │ M4  │ │ M5  │                      │   │
│  │  │Master│ │     │ │     │ │     │ │     │                      │   │
│  │  └─────┘ └─────┘ └─────┘ └─────┘ └─────┘                      │   │
│  │    Node1   Node2   Node3   Node4   Node5                       │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                              │                                          │
│                              ▼ 网络分区                                  │
│                                                                         │
│  脑裂状态(未正确配置时):                                              │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  分区 A:                        分区 B:                       │   │
│  │  ┌─────┐ ┌─────┐ ┌─────┐       ┌─────┐ ┌─────┐                │   │
│  │  │ M1  │ │ M2  │ │ M3  │       │ M4  │ │ M5  │                │   │
│  │  │Master│ │     │ │     │       │Master│ │     │  ← 两个Master!│   │
│  │  └─────┘ └─────┘ └─────┘       └─────┘ └─────┘                │   │
│  │                                                                 │   │
│  │  问题:                                                         │   │
│  │  • 两个分区各自处理请求                                         │   │
│  │  • 数据分叉,无法合并                                           │   │
│  │  • 可能导致数据丢失                                             │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

ES 7.x+ 如何避免脑裂

┌─────────────────────────────────────────────────────────────────────────┐
│                    ES 7.x+ 脑裂避免机制                                  │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  自动计算 minimum_master_nodes:                                        │
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  Master-eligible 节点数    minimum_master_nodes                │   │
│  │  ──────────────────────────────────────────────                │   │
│  │  1                          1                                  │   │
│  │  2                          2                                  │   │
│  │  3                          2                                  │   │
│  │  4                          3                                  │   │
│  │  5                          3                                  │   │
│  │  N                          floor(N/2) + 1                     │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  网络分区后:                                                            │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  分区 A(3 节点):               分区 B(2 节点):             │   │
│  │  ┌─────┐ ┌─────┐ ┌─────┐        ┌─────┐ ┌─────┐               │   │
│  │  │ M1  │ │ M2  │ │ M3  │        │ M4  │ │ M5  │               │   │
│  │  │Master│ │     │ │     │        │ 无   │ │     │               │   │
│  │  └─────┘ └─────┘ └─────┘        └─────┘ └─────┘               │   │
│  │     3 ≥ minimum_master_nodes(3)    2 < minimum_master_nodes(3) │   │
│  │     可以维持 Master                无法选出 Master              │   │
│  │                                                                 │   │
│  │  结果:只有分区 A 能继续工作,避免了脑裂                         │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

最佳实践

# 配置建议

# 1. 奇数个 Master-eligible 节点
# 推荐:3 个(可容忍 1 个故障)
#      5 个(可容忍 2 个故障)

# 2. 专用 Master 节点
node.roles: [ master ]

# 3. 设置合理的超时
discovery.zen.ping_timeout: 5s
discovery.zen.join_timeout: 60s

# 4. 配置初始 Master 候选节点
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]

# 5. 单播发现
discovery.seed_hosts: ["node-1", "node-2", "node-3"]

故障检测

故障检测机制

┌─────────────────────────────────────────────────────────────────────────┐
│                    故障检测机制                                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  两种检测:                                                              │
│                                                                         │
│  1. Master 检测节点故障                                                 │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  Master 定期向所有节点发送 Ping                                 │   │
│  │  如果节点未响应,标记为故障                                     │   │
│  │                                                                 │   │
│  │  配置:                                                         │   │
│  │  • ping_interval: 1s(发送间隔)                                │   │
│  │  • ping_timeout: 30s(响应超时)                                │   │
│  │  • ping_retries: 3(重试次数)                                  │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  2. 节点检测 Master 故障                                                │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  所有节点定期检查 Master 是否存活                               │   │
│  │  如果 Master 无响应,触发重新选举                               │   │
│  │                                                                 │   │
│  │  配置:                                                         │   │
│  │  • cluster.fault_detection.leader_check.interval: 1s           │   │
│  │  • cluster.fault_detection.leader_check.timeout: 10s           │   │
│  │  • cluster.fault_detection.leader_check.retry_count: 3         │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

故障恢复流程

┌─────────────────────────────────────────────────────────────────────────┐
│                    故障恢复流程                                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  节点故障后的恢复:                                                      │
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  Step 1: 检测故障                                              │   │
│  │  ┌───────────────────────────────────────────────────────────┐ │   │
│  │  │ Master 检测到 Node 3 故障                                 │ │   │
│  │  └───────────────────────────────────────────────────────────┘ │   │
│  │                                                                 │   │
│  │  Step 2: 更新集群状态                                          │   │
│  │  ┌───────────────────────────────────────────────────────────┐ │   │
│  │  │ • 将 Node 3 标记为离开                                    │ │   │
│  │  │ • 更新分片路由表                                           │ │   │
│  │  │ • 副本分片提升为主分片(如需要)                           │ │   │
│  │  └───────────────────────────────────────────────────────────┘ │   │
│  │                                                                 │   │
│  │  Step 3: 重新分配分片                                          │   │
│  │  ┌───────────────────────────────────────────────────────────┐ │   │
│  │  │ • 将丢失的主分片从副本提升                                 │ │   │
│  │  │ • 创建新的副本分片                                         │ │   │
│  │  │ • 分片数据从其他节点复制                                   │ │   │
│  │  └───────────────────────────────────────────────────────────┘ │   │
│  │                                                                 │   │
│  │  Step 4: 恢复完成                                              │   │
│  │  ┌───────────────────────────────────────────────────────────┐ │   │
│  │  │ • 集群状态变为 Green                                       │ │   │
│  │  │ • 所有分片已分配                                           │ │   │
│  │  └───────────────────────────────────────────────────────────┘ │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

数据一致性保证

写入一致性

┌─────────────────────────────────────────────────────────────────────────┐
│                    写入一致性级别                                        │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  consistency 参数:                                                      │
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  one(默认)                                                    │   │
│  │  • 主分片写入成功即可返回                                       │   │
│  │  • 性能最高,但可能有数据丢失风险                               │   │
│  │                                                                 │   │
│  │  quorum                                                        │   │
│  │  • 需要 majority 分片写入成功                                   │   │
│  │  • majority = (primary + replicas) / 2 + 1                      │   │
│  │  • 平衡性能和可靠性                                             │   │
│  │                                                                 │   │
│  │  all                                                           │   │
│  │  • 所有分片(主分片 + 所有副本)写入成功                        │   │
│  │  • 可靠性最高,性能最低                                         │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  示例:                                                                  │
│  PUT /my_index/_doc/1?consistency=quorum&wait_for_active_shards=2      │
│  {                                                                      │
│    "title": "Document"                                                 │
│  }                                                                      │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

副本同步

┌─────────────────────────────────────────────────────────────────────────┐
│                    副本同步机制                                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  写入流程:                                                              │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  1. 文档写入主分片                                              │   │
│  │  2. 主分片写入 Translog                                         │   │
│  │  3. 主分片将操作发送到所有副本分片                              │   │
│  │  4. 副本分片写入本地 Translog                                   │   │
│  │  5. 副本分片确认写入                                            │   │
│  │  6. 主分片返回成功响应                                          │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  同步模型:                                                              │
│                                                                         │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │                                                                │    │
│  │  Primary Shard                                                 │    │
│  │       │                                                        │    │
│  │       │ 写入操作                                               │    │
│  │       ▼                                                        │    │
│  │  ┌─────────┐                                                   │    │
│  │  │ Translog│                                                   │    │
│  │  └─────────┘                                                   │    │
│  │       │                                                        │    │
│  │       ├──────────────┬──────────────┐                         │    │
│  │       ▼              ▼              ▼                         │    │
│  │  ┌─────────┐   ┌─────────┐   ┌─────────┐                     │    │
│  │  │Replica 1│   │Replica 2│   │Replica 3│                     │    │
│  │  │ Translog│   │ Translog│   │ Translog│                     │    │
│  │  └─────────┘   └─────────┘   └─────────┘                     │    │
│  │       │              │              │                         │    │
│  │       └──────────────┴──────────────┘                         │    │
│  │                      │                                        │    │
│  │                      ▼                                        │    │
│  │                 确认写入                                       │    │
│  │                                                                │    │
│  └────────────────────────────────────────────────────────────────┘    │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Sequence ID

┌─────────────────────────────────────────────────────────────────────────┐
│                    Sequence ID 机制                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ES 6.0+ 引入 Sequence ID 解决副本一致性问题                             │
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  每个操作分配唯一的递增 ID:                                    │   │
│  │                                                                 │   │
│  │  Operation 1: seq_no=0, primary_term=1                         │   │
│  │  Operation 2: seq_no=1, primary_term=1                         │   │
│  │  Operation 3: seq_no=2, primary_term=1                         │   │
│  │  ...                                                            │   │
│  │                                                                 │   │
│  │  primary_term:主分片任期,每次主分片变更后递增                  │   │
│  │  seq_no:操作序号,每个主分片内递增                             │   │
│  │                                                                 │   │
│  │  用途:                                                         │   │
│  │  • 副本按顺序重放操作                                           │   │
│  │  • 检测和恢复数据不一致                                         │   │
│  │  • 优化恢复过程(只同步差异)                                   │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

高可用配置最佳实践

生产环境配置

# elasticsearch.yml

# 集群名称(所有节点相同)
cluster.name: production-cluster

# 节点名称(每个节点唯一)
node.name: node-1

# 角色配置
node.roles: [ master, data ]

# 初始 Master 候选节点
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]

# 发现配置
discovery.seed_hosts: ["node-1", "node-2", "node-3"]

# 网络配置
network.host: 0.0.0.0
http.port: 9200
transport.port: 9300

# 故障检测
discovery.zen.ping_timeout: 5s
discovery.zen.join_timeout: 60s

# 内存锁定
bootstrap.memory_lock: true

# 索引恢复设置
indices.recovery.max_bytes_per_sec: 100mb
cluster.routing.allocation.node_concurrent_recoveries: 4

监控指标

┌─────────────────────────────────────────────────────────────────────────┐
│                    关键监控指标                                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  集群健康:                                                              │
│  GET /_cluster/health                                                   │
│  • status: green/yellow/red                                            │
│  • number_of_nodes                                                      │
│  • unassigned_shards                                                    │
│                                                                         │
│  节点状态:                                                              │
│  GET /_cat/nodes?v&h=name,role,heap.percent,ram.percent,load_1m        │
│                                                                         │
│  分片状态:                                                              │
│  GET /_cat/shards?v&state=UNASSIGNED                                   │
│                                                                         │
│  Pending Tasks:                                                         │
│  GET /_cluster/pending_tasks                                            │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

总结

本章深入解析了 ES 分布式一致性机制:

要点说明
选举机制ES 7.x+ 类 Raft 算法,自动计算 quorum
脑裂避免minimum_master_nodes = N/2 + 1
故障检测Master 检测节点 + 节点检测 Master
数据一致性Translog + Sequence ID 保证

参考资料

下一章预告

下一章将探讨 生产实践与性能调优,包括:

  • 硬件选型与 JVM 调优
  • 索引设计最佳实践
  • 性能监控与调优
  • 常见问题排查
分享: