5月27日 23:52

Elasticsearch 如何实现高可用和容灾备份?

Elasticsearch 在日志分析、全文检索、可观测性等场景中承担核心存储角色,一旦集群不可用,下游查询和写入全部中断。高可用保证单节点/单机房故障后服务继续运行,容灾备份保证数据在区域性灾难后可恢复。两者机制不同,缺一不可。

高可用:集群内故障自愈

分片与副本——数据冗余的基石

Elasticsearch 将每个索引拆分为多个主分片(primary shard),每个主分片可配置若干副本分片(replica shard)。主分片与副本分片分布在不同节点上:

  • 主分片故障:副本自动提升为新主分片,数据零丢失,查询不中断。
  • 副本分片故障:主分片仍在,集群自动在其他节点重建副本。
  • 动态调整:副本数可在索引运行时修改,主分片数创建后不可更改,需提前规划。
json
PUT /my_index { "settings": { "number_of_shards": 3, "number_of_replicas": 1 } }

生产环境建议 number_of_replicas >= 1,关键业务设为 2,可容忍单节点故障且仍有冗余。但副本越多写入吞吐越低(每个写操作需同步到所有副本),需在可用性与性能间取舍。

节点角色分离

生产集群至少 3 节点,建议按角色分离:

角色配置职责最低数量
专用主节点node.master: true, node.data: false集群管理、元数据维护3
数据节点node.master: false, node.data: true存储分片数据、执行 CRUD按数据量扩容
协调节点node.master: false, node.data: false请求路由、结果聚合2+

专用主节点不存数据、不处理查询,资源占用低但保障选主稳定。只有 2 个候选主节点时容易出现选不出 master 的问题,必须保证奇数个候选节点。

脑裂防护

网络分区可能导致两个子集群各自选主,产生"脑裂",数据不一致。Elasticsearch 7.x+ 已废弃 discovery.zen.minimum_master_nodes,改为自动计算法定人数(quorum),但理解其原理仍然关键:

  • 7.x 之前:手动设置 discovery.zen.minimum_master_nodes(候选主节点数 / 2) + 1,确保只有多数派能选主。
  • 7.x+:由集群自动管理,但前提是正确配置 cluster.initial_master_nodes,首次启动时指定初始主节点列表。
yaml
# elasticsearch.yml — 首次启动配置 discovery.seed_hosts: ["es-node1", "es-node2", "es-node3"] cluster.initial_master_nodes: ["es-node1", "es-node2", "es-node3"]

集群健康与故障恢复

集群状态直观反映可用性:

  • green:所有主分片和副本分片正常。
  • yellow:主分片正常,部分副本缺失(单节点故障时常见,服务仍可用)。
  • red:部分主分片不可用,数据有丢失风险。
bash
# 查看集群健康 curl -XGET "http://localhost:9200/_cluster/health?pretty" # 查看分片分配情况 curl -XGET "http://localhost:9200/_cat/shards?v"

节点故障后,集群自动执行分片重平衡:提升副本为主分片 → 在存活节点重建副本 → 数据重新均衡。此过程对应用透明,但重平衡期间查询性能可能下降。

容灾备份:跨机房/跨区域数据保护

高可用解决的是集群内单点故障,但整个机房故障(断电、网络中断、自然灾害)需要容灾方案。Elasticsearch 提供两条路径:快照恢复(冷备份)和跨集群复制 CCR(热备份)。

快照与恢复(Snapshot & Restore)

快照将索引数据备份到外部存储(本地磁盘、S3、HDFS 等),支持增量备份和按时间点恢复。

1. 注册快照仓库

json
PUT /_snapshot/my_backup { "type": "fs", "settings": { "location": "/var/backups/elasticsearch" } }

S3 仓库需要安装 repository-s3 插件:

json
PUT /_snapshot/s3_backup { "type": "s3", "settings": { "bucket": "my-backup-bucket", "region": "us-east-1", "base_path": "es-snapshots" } }

2. 创建快照

bash
curl -XPUT "http://localhost:9200/_snapshot/my_backup/snapshot-20260527" \ -H "Content-Type: application/json" -d '{ "indices": "*,-.monitoring*,-.security*", "ignore_unavailable": true, "include_global_state": false }'

注意排除系统索引(.monitoring*.security*.ds* 等),避免恢复时覆盖集群安全配置。

3. 自动定期备份

通过 SLM(Snapshot Lifecycle Management,8.x 内置)自动执行:

json
PUT /_slm/policy/daily-snapshots { "schedule": "0 30 2 * * ?", "name": "<daily-snap-{now/d}>", "repository": "my_backup", "config": { "indices": ["*", "-.monitoring*", "-.security*"], "ignore_unavailable": true, "include_global_state": false }, "retention": { "expire_after": "30d", "min_count": 5, "max_count": 50 } }

4. 从快照恢复

json
POST /_snapshot/my_backup/snapshot-20260527/_restore { "indices": "my_index", "include_aliases": true }

恢复时目标索引必须不存在(或使用 rename_pattern 重命名)。整个集群不可用时,需先重建集群再恢复快照。

跨集群复制 CCR(Cross-Cluster Replication)

CCR 是 Elasticsearch 白金版功能,实现主集群到从集群的近实时索引复制,适用于异地容灾和读写分离。

工作流程

  1. 配置远程集群:在从集群中声明主集群的连接信息。
  2. 创建 Follower 索引:从集群以只读方式持续拉取主集群的变更(先全量复制 segment,再增量同步 translog)。
  3. 灾难切换:主集群不可用时,将 Follower 索引转为普通索引(POST /follower_index/_ccr/unfollow),接管读写流量。
json
PUT /_cluster/settings { "persistent": { "cluster": { "remote": { "leader-cluster": { "seeds": ["10.0.1.10:9300"] } } } } }
json
PUT /follower_index/_ccr/follow { "remote_cluster": "leader-cluster", "leader_index": "leader_index" }

关键限制

  • 需要白金版许可证。
  • 从集群版本必须 >= 主集群版本。
  • Follower 索引只读,需 unfollow 后才可写入。
  • ccr.indices.recovery.max_bytes_per_sec 控制复制带宽(默认 40MB/s)。

快照 vs CCR 对比

维度快照恢复CCR
数据延迟分钟~小时级(取决于备份频率)秒级近实时
恢复速度需重建索引,分钟~小时级秒级切换
成本低(对象存储)高(需独立集群 + 白金许可)
适用场景数据归档、时间点恢复、开发测试异地热备、业务连续性要求高
许可证基础版即可白金版

生产环境建议两者结合:CCR 保障实时容灾,快照提供长期归档和时间点回溯能力。

生产环境关键配置清单

防止数据丢失

yaml
# elasticsearch.yml # 每个索引默认至少 1 个副本 index.number_of_replicas: 1 # 刷新间隔,写入密集场景可适当增大 index.refresh_interval: 1s # Translog 持久化策略:每次写操作后 fsync index.translog.durability: request

索引生命周期管理(ILM)

ILM 自动管理索引的分片数、副本数、迁移和删除,避免冷数据无限膨胀:

json
PUT /_ilm/policy/hot-warm-delete { "policy": { "phases": { "hot": { "min_age": "0ms", "actions": { "rollover": { "max_age": "7d", "max_primary_shard_size": "50gb" } } }, "warm": { "min_age": "30d", "actions": { "shrink": { "number_of_shards": 1 }, "forcemerge": { "max_num_segments": 1 }, "allocate": { "require": { "data": "warm" } } } }, "delete": { "min_age": "90d", "actions": { "delete": {} } } } } }

热节点用 SSD 存储近期活跃数据,温节点用 SATA 存储历史数据,ILM 自动将索引从热节点迁移到温节点,90 天后自动删除。冷热分层可降低 40%~60% 存储成本。

容灾演练

容灾方案不演练等于没有。建议每季度执行:

  1. 节点级:关闭一个数据节点,观察副本提升和集群重平衡。
  2. 索引级:删除一个索引,从快照恢复,验证数据完整性(对比文档数 _count)。
  3. 集群级:主集群断网,将 CCR Follower unfollow 接管,验证读写正常。
bash
# 验证恢复后文档数一致 curl -XGET "http://localhost:9200/my_index/_count"

面试追问方向

  • RPO 和 RTO 分别是什么? RPO(Recovery Point Objective)是可接受的数据丢失量,RTO(Recovery Time Objective)是可接受的服务中断时长。快照方案的 RPO 取决于备份频率,CCR 的 RPO 为秒级。
  • 副本数设为 2 写入性能下降多少? 通常下降 30%~40%,因为每次写操作需同步到主分片 + 2 个副本。写密集场景可设为 1 个副本,读密集场景增加副本数提升吞吐。
  • 主分片数为什么不能改? 主分片数决定了文档的路由公式 shard = hash(routing) % number_of_primary_shards,修改后所有文档的路由全部失效。扩容只能通过创建新索引 + reindex 实现。
  • CCR 和 Snapshot 能否替代彼此? 不能。CCR 是实时热备但无法回溯历史时间点,Snapshot 是冷备但支持时间点恢复和长期归档。两者互补。
标签:ElasticSearch