一、为什么Elasticsearch会丢失数据?

Elasticsearch虽然是个靠谱的搜索引擎,但数据丢失的情况并不少见。最常见的原因是节点宕机、磁盘损坏或者误操作。比如你刚存进去100万条数据,结果第二天发现少了20万条,这时候别急着怀疑人生,先看看是不是这些原因导致的。

举个真实案例:某电商平台的商品索引突然少了30%的数据。后来发现是因为运维同学手滑执行了错误的索引删除命令。这种情况其实完全可以通过权限控制避免,但人总会犯错嘛。

二、预防胜于治疗 - 基础防护措施

1. 副本分片不是摆设

创建索引时一定要设置合理的副本数。比如这样:

// 技术栈:Elasticsearch Java API
CreateIndexRequest request = new CreateIndexRequest("products");
request.settings(Settings.builder()
    .put("index.number_of_shards", 3)  // 主分片数
    .put("index.number_of_replicas", 2)  // 每个主分片的副本数
);
client.indices().create(request, RequestOptions.DEFAULT);

这个配置意味着数据会被分散在3个主分片上,每个主分片还有2个备份。即使挂掉1个节点,数据也不会丢失。

2. 定期快照不能少

就像给手机备份一样,Elasticsearch也需要定期快照:

// 技术栈:Elasticsearch Java API
// 1. 先创建仓库
PutRepositoryRequest repositoryRequest = new PutRepositoryRequest("my_backup");
repositoryRequest.type("fs");
repositoryRequest.settings(Settings.builder()
    .put("location", "/mnt/backups")
    .put("compress", true)
);
client.snapshot().createRepository(repositoryRequest, RequestOptions.DEFAULT);

// 2. 创建快照
CreateSnapshotRequest snapshotRequest = new CreateSnapshotRequest("my_backup", "snapshot_20230601");
snapshotRequest.indices("products");
client.snapshot().create(snapshotRequest, RequestOptions.DEFAULT);

建议至少每天做一次快照,重要数据可以每小时一次。

三、数据丢了怎么办 - 恢复实战指南

1. 从副本恢复

如果只是某个节点挂了,等它重新加入集群后,数据会自动从副本同步。但要注意:

// 技术栈:Elasticsearch Java API
// 检查未分配的分片
ClusterHealthRequest healthRequest = new ClusterHealthRequest()
    .waitForNoRelocatingShards(true)
    .timeout("5m");
ClusterHealthResponse healthResponse = client.cluster().health(healthRequest, RequestOptions.DEFAULT);

if (healthResponse.getUnassignedShards() > 0) {
    // 有分片未分配,需要人工干预
    System.out.println("警告:有未分配的分片!");
}

2. 从快照恢复

这是最靠谱的恢复方式:

// 技术栈:Elasticsearch Java API
RestoreSnapshotRequest restoreRequest = new RestoreSnapshotRequest("my_backup", "snapshot_20230601");
restoreRequest.indices("products");  // 只恢复products索引
restoreRequest.renamePattern("products_(.+)");
restoreRequest.renameReplacement("restored_products_$1");
client.snapshot().restore(restoreRequest, RequestOptions.DEFAULT);

恢复时建议新建索引,不要直接覆盖现有索引,避免二次伤害。

四、进阶防护 - 这些坑你别踩

1. 别让translog坑了你

Elasticsearch靠translog保证数据安全,但配置不当会出问题:

// 技术栈:Elasticsearch Java API
IndexRequest request = new IndexRequest("products");
request.source("{\"name\":\"新款手机\"}", XContentType.JSON);
request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);  // 强制刷新
client.index(request, RequestOptions.DEFAULT);

频繁强制刷新会影响性能,但不刷新又可能丢数据。建议根据业务需求平衡:

  • 重要数据:设置index.translog.durabilityrequest
  • 普通数据:设为async并适当调整sync_interval

2. 监控不能停

这些指标要重点关注:

// 技术栈:Elasticsearch Java API
NodesStatsRequest nodesStatsRequest = new NodesStatsRequest();
nodesStatsRequest.setFs(true);  // 磁盘使用情况
nodesStatsRequest.setIndices(true);  // 索引状态

NodesStatsResponse response = client.nodes().stats(nodesStatsRequest, RequestOptions.DEFAULT);
for (NodeStats nodeStats : response.getNodes()) {
    FsInfo.Path path = nodeStats.getFs().getTotal();
    System.out.println("磁盘剩余空间:" + path.getAvailable());
    
    IndicesStats indicesStats = nodeStats.getIndices();
    System.out.println("索引数:" + indicesStats.getIndexCount());
}

建议设置告警阈值:磁盘空间低于20%就要扩容,未分配分片数大于0立即报警。

五、特殊场景处理方案

1. 误删数据怎么救

如果手滑执行了删除操作,别慌:

// 技术栈:Elasticsearch Java API
// 1. 立即停止相关索引的写入
UpdateSettingsRequest settingsRequest = new UpdateSettingsRequest("products");
Settings settings = Settings.builder()
    .put("index.blocks.write", true)
    .build();
settingsRequest.settings(settings);
client.indices().putSettings(settingsRequest, RequestOptions.DEFAULT);

// 2. 从快照恢复单个文档
SearchRequest searchRequest = new SearchRequest("snapshot_my_backup/snapshot_20230601");
searchRequest.source().query(QueryBuilders.idsQuery().addIds("product_123"));
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
// 找到文档后重新插入

2. 跨集群数据同步

多集群环境下如何保证数据安全:

// 技术栈:Elasticsearch Java API
// 使用CCR功能设置跟随者索引
PutAutoFollowPatternRequest request = new PutAutoFollowPatternRequest("my_pattern");
request.setRemoteCluster("remote_cluster");
request.setLeaderIndexPatterns("products");
request.setFollowIndexNamePattern("{{leader_index}}_copy");
client.ccr().putAutoFollowPattern(request, RequestOptions.DEFAULT);

这样主集群的数据会自动同步到备份集群,相当于实时备份。

六、最佳实践总结

  1. 3-2-1备份原则:至少保留3份数据,用2种不同存储形式,其中1份异地保存
  2. 定期演练:每季度做一次恢复演练,确保备份可用
  3. 分级保护:核心数据采用最高级别保护,非核心数据可以适当放宽
  4. 文档化流程:把恢复步骤写成文档,紧急情况下照着做

记住,数据安全没有银弹,需要根据业务特点制定合适的策略。希望这些经验能帮你少踩坑,遇到数据丢失时也能从容应对。