1. 问题定位:数据丢失的常见成因

咱们先来盘盘ES集群重启后数据丢失的典型场景。最近遇到个生产案例:某电商平台凌晨执行集群滚动重启后,发现商品索引丢失了3小时的数据。运维团队排查发现节点重启时未完成段合并操作,translog同步机制也未正确配置。

通过GET _cat/indices?v命令查看分片状态时,发现多个分片处于UNASSIGNED状态:

# 查看分片分配异常(注意红框标记的UNASSIGNED)
health status index        uuid                   pri rep docs.count 
yellow open   products     ABD2dFj3QeS3WXKpZ7D0Gw   5   1     0 

关键日志定位技巧:

grep "failed to recover" /var/log/elasticsearch/cluster.log
# 典型错误示例:
[2023-08-20T03:15:12] org.elasticsearch.index.shard.IndexShardRecoveryException: failed to recover...

2. 数据恢复的四大核心策略

2.1 快照恢复(黄金方案)

适用场景:定期备份已配置,但未及时执行最终快照

创建S3仓库配置示例:

PUT _snapshot/my_s3_repository
{
  "type": "s3",
  "settings": {
    "bucket": "es-backup-2023",
    "region": "ap-east-1",
    "base_path": "prod-cluster/"
  }
}

# 立即执行快照(注意replace_existing参数)
PUT _snapshot/my_s3_repository/snapshot_20230820_emergency?wait_for_completion=true
{
  "indices": "products",
  "ignore_unavailable": true,
  "include_global_state": false
}

C#自动恢复实现(使用Elasticsearch.Net):

var settings = new ConnectionConfiguration(new Uri("http://localhost:9200"))
    .RequestTimeout(TimeSpan.FromMinutes(10));

var client = new ElasticLowLevelClient(settings);

// 恢复快照请求体
var restoreParams = new {
    indices = "products",
    rename_pattern = "(.+)",
    rename_replacement = "restored_$1"
};

var response = client.Snapshot.Restore<DynamicResponse>(
    "my_s3_repository", 
    "snapshot_20230820_emergency",
    PostData.Serializable(restoreParams));

if (!response.Success)
{
    // 重试逻辑和异常处理
    Console.WriteLine($"恢复失败:{response.DebugInformation}");
}

优缺点分析

  • 优势:数据完整性最佳,支持跨集群恢复
  • 劣势:依赖前期备份策略,大索引恢复耗时

2.2 副本分片重建

救急场景:主分片损坏但副本可用

动态调整副本数:

# 先关闭索引避免写入冲突
POST products/_close

# 重置副本数触发恢复
PUT products/_settings
{
  "index.number_of_replicas": 2
}

# 重新打开索引
POST products/_open

C#动态调整副本实现:

var updateSettings = client.Indices.UpdateSettings("products", u => u
    .Index("products")
    .Settings(s => s
        .NumberOfReplicas(2))
    );

if (!updateSettings.IsValid)
{
    // 报警通知运维人员
    SendAlert($"副本数调整失败:{updateSettings.DebugInformation}");
}

操作要点

  • 确保集群有足够节点承载新副本
  • 监控_cluster/health的active_shards_percent指标

2.3 手动重建索引

适用情况:存在原始数据源但索引结构损坏

使用_reindex API抢救数据:

POST _reindex
{
  "source": {
    "index": "products_broken",
    "query": {
      "range": {
        "@timestamp": {
          "gte": "now-3h"
        }
      }
    }
  },
  "dest": {
    "index": "products_recovered",
    "op_type": "create"
  }
}

避坑指南

  • 设置scroll_size=1000平衡性能与内存
  • 添加wait_for_completion=false异步执行

2.4 Translog恢复黑科技

最后防线:找回未持久化的操作记录

强制translog恢复操作:

# 检查未提交的translog条目
GET /_cat/translog?v&index=products

# 执行translog重放
POST products/_flush?wait_if_ongoing=true

关键配置调整:

# elasticsearch.yml
index.translog.sync_interval: "30s"  # 默认5s,根据业务容忍度调整
index.translog.durability: "async"   # 高风险配置!需配合UPSERT操作

3. 技术选型决策树

3.1 恢复方案对比矩阵

方案类型 恢复耗时 数据完整性 操作复杂度 前置条件
快照恢复 完美 简单 有效快照
副本重建 依赖副本数 中等 健康副本存在
手动重建 极长 不确定 复杂 有原始数据源
Translog恢复 最短 可能丢失 高风险 未持久化日志存在

3.2 场景化选择指南

  • 金融交易数据 → 快照恢复+translog双保险
  • 日志类数据 → 副本重建优先
  • 实时监控数据 → Translog恢复+手动补数

4. 防患未然的配置建议

4.1 必须检查的配置项

# 生产环境推荐配置
cluster.routing.allocation.enable: all
indices.recovery.max_bytes_per_sec: 200mb
node_concurrent_recoveries: 3

4.2 重启操作checklist

  1. 预检查:GET _cluster/health?pretty
  2. 禁用分片分配:PUT _cluster/settings {"transient": {"cluster.routing.allocation.enable": "none"}}
  3. 执行同步刷新:POST _flush/synced
  4. 逐节点滚动重启

5. 总结与经验沉淀

经过多个生产环境的实战验证,我们总结出以下血泪经验:

  1. 备份策略:快照保留周期建议采用"7-3-1"原则(7天日备、3周周备、1年月备)
  2. 监控预警:设置active_shards_percent<95%的报警阈值
  3. 压力测试:定期模拟断电演练,实测恢复流程可靠性
  4. 文档沉淀:维护《ES灾难恢复手册》,每季度更新版本

最后给各位ES老司机提个醒:每次大版本升级后,务必用_shadowAPI测试恢复流程。记住,没有经过验证的备份等于没有备份!