【ElasticSearch从入门到架构师】第15章:生产常见故障排查与优化实战

导读:在生产环境中,ElasticSearch集群难免会遇到各种故障。本章将深入讲解五大类常见生产故障的排查思路和解决方案,帮助你快速定位问题、恢复服务,并建立长效的优化机制。无论你是运维工程师还是ES架构师,这些实战经验都将助力你从容应对生产挑战。


一、集群红色、黄色状态故障排查

1.1 状态含义与影响

ElasticSearch集群健康状态分为三种:

状态 含义 影响
绿色(Green) 所有主分片和副本分片都已分配 集群完全正常
黄色(Yellow) 所有主分片已分配,但部分副本分片未分配 数据完整性OK,高可用有风险
红色(Red) 部分主分片未分配 部分数据不可用,严重影响业务

1.2 黄色状态排查步骤

步骤1:查看集群健康详情

bash 复制代码
curl -X GET "localhost:9200/_cluster/health?pretty"
curl -X GET "localhost:9200/_cat/indices?v"
curl -X GET "localhost:9200/_cat/shards?h=index,shard,prirep,state,unassigned.reason"

步骤2:分析未分配原因

bash 复制代码
# 查看未分配分片的详细原因
curl -X GET "localhost:9200/_cluster/allocation/explain?pretty"

常见原因及解决方案:

原因 说明 解决方案
NODE_LEFT 节点离开集群 等待节点恢复,或手动分配
REPLICA_ADDED 新增副本 等待自动分配,或检查节点磁盘
INDEX_CREATED 新建索引 等待自动分配
CLUSTER_RECOVERED 集群恢复 等待自动分配
磁盘空间不足 节点磁盘使用率>85% 清理磁盘或增加节点
节点数量不足 副本数 > 可用节点数 增加节点或调整副本数

步骤3:手动分配未分配分片(紧急情况)

bash 复制代码
# 方法1:重新路由分片
curl -X POST "localhost:9200/_cluster/reroute" -H 'Content-Type: application/json' -d'
{
  "commands": [
    {
      "allocate_empty_primary": {
        "index": "your_index",
        "shard": 0,
        "node": "target_node_name",
        "accept_data_loss": true
      }
    }
  ]
}'

# 方法2:减少副本数(临时方案)
curl -X PUT "localhost:9200/your_index/_settings" -H 'Content-Type: application/json' -d'
{
  "index": {
    "number_of_replicas": 0
  }
}'

1.3 红色状态排查步骤

步骤1:定位问题索引

bash 复制代码
curl -X GET "localhost:9200/_cat/indices?v&health=red"

步骤2:检查主分片状态

bash 复制代码
curl -X GET "localhost:9200/_cat/shards/your_index?v"
curl -X GET "localhost:9200/_cluster/allocation/explain?pretty"

步骤3:恢复策略

场景 恢复方案
节点宕机 重启节点,等待自动恢复
分片损坏 从副本恢复,或重建索引
数据丢失 从快照恢复,或重新导入数据
磁盘故障 更换磁盘,从副本恢复

强制分配主分片(数据会丢失,慎用)

bash 复制代码
curl -X POST "localhost:9200/_cluster/reroute" -H 'Content-Type: application/json' -d'
{
  "commands": [
    {
      "allocate_empty_primary": {
        "index": "your_index",
        "shard": 0,
        "node": "target_node_name",
        "accept_data_loss": true
      }
    }
  ]
}'

1.4 预防措施

bash 复制代码
# 1. 设置最小主节点数(防止脑裂)
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "discovery.zen.minimum_master_nodes": 2
  }
}'

# 2. 启用副本自动分配
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster.routing.allocation.enable": "all"
  }
}'

# 3. 设置磁盘水位线
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster.routing.allocation.disk.watermark.low": "85%",
    "cluster.routing.allocation.disk.watermark.high": "90%",
    "cluster.routing.allocation.disk.watermark.flood_stage": "95%"
  }
}'

二、写入卡顿、查询超时、接口响应缓慢解决

2.1 问题诊断流程

复制代码
用户反馈慢
    ↓
查看监控指标(CPU、内存、磁盘IO、网络)
    ↓
分析慢查询日志
    ↓
检查索引设置与映射
    ↓
优化方案实施
    ↓
验证效果

2.2 写入性能优化

2.2.1 批量写入优化

错误示范

java 复制代码
// 单条写入,性能极差
for (Document doc : documents) {
    esClient.index(indexRequest);
}

正确示范

java 复制代码
// 批量写入,推荐
BulkRequest bulkRequest = new BulkRequest();
for (Document doc : documents) {
    bulkRequest.add(new IndexRequest("index").id(doc.getId()).source(doc.toMap()));
}
if (bulkRequest.numberOfActions() > 0) {
    esClient.bulk(bulkRequest);
}

最佳实践

  • 批量大小:5-15MB,1000-5000条文档
  • 并发数:根据集群规模调整,通常2-4个并发
  • 刷新间隔:写入期间设置为-1,写入完成后恢复
bash 复制代码
# 写入前调整设置
curl -X PUT "localhost:9200/your_index/_settings" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "-1",
    "number_of_replicas": 0
  }
}'

# 写入完成后恢复
curl -X PUT "localhost:9200/your_index/_settings" -H 'Content-Type: application/json' -d'
{
  "index": {
    "refresh_interval": "30s",
    "number_of_replicas": 1
  }
}'

# 强制刷新
curl -X POST "localhost:9200/your_index/_refresh"
2.2.2 索引优化
优化项 说明 配置示例
关闭不必要的功能 减少写入开销 "index_options": "docs"
使用自动ID 避免ID冲突检查 不指定_id
调整分片数 避免过多分片 "number_of_shards": 3
使用SSD 提升IO性能 硬件层面优化

2.3 查询性能优化

2.3.1 慢查询日志分析
bash 复制代码
# 启用慢查询日志
curl -X PUT "localhost:9200/your_index/_settings" -H 'Content-Type: application/json' -d'
{
  "index": {
    "search.slowlog.threshold.query.warn": "10s",
    "search.slowlog.threshold.query.info": "5s",
    "search.slowlog.threshold.fetch.warn": "5s",
    "search.slowlog.threshold.fetch.info": "3s"
  }
}'

# 查看慢查询日志
tail -f /var/log/elasticsearch/${cluster.name}_index_search_slowlog.log
2.3.2 查询优化技巧

1. 使用过滤器(Filter)代替查询(Query)

json 复制代码
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "elasticsearch" }}
      ],
      "filter": [
        { "term": { "status": 1 }},
        { "range": { "create_time": { "gte": "2024-01-01" }}}
      ]
    }
  }
}

原理:Filter不计算评分,结果可缓存,性能远优于Query。

2. 避免通配符查询

json 复制代码
// 错误:前缀通配符导致全表扫描
{ "wildcard": { "name": { "value": "*smith" }}}

// 正确:使用倒排索引
{ "match": { "name": "smith" }}

3. 分页优化

json 复制代码
// 浅分页(前1000条)
{ "from": 0, "size": 10 }

// 深分页(超过10000条,使用scroll或search_after)
// 方法1:scroll API
{ "scroll": "1m" }

// 方法2:search_after(推荐)
{ "search_after": [上次查询的sort值] }

4. 只返回需要的字段

json 复制代码
{
  "_source": ["title", "author", "create_time"],
  "query": { "match_all": {}}
}
2.3.3 索引设计优化
优化策略 实施方法
合理使用分词器 不需要分词的字段使用keyword类型
避免过度索引 只索引需要搜索的字段
使用别名 方便索引重建和切换
冷热分离 热数据用SSD,冷数据用HDD
bash 复制代码
# 创建索引模板
curl -X PUT "localhost:9200/_index_template/logs_template" -H 'Content-Type: application/json' -d'
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1
    },
    "mappings": {
      "properties": {
        "message": { "type": "text" },
        "level": { "type": "keyword" },
        "timestamp": { "type": "date" }
      }
    }
  }
}'

三、内存飙升、GC频繁、OOM问题根治

3.1 内存结构分析

ElasticSearch内存使用分为以下几部分:

复制代码
ES内存使用
├── JVM Heap(通常设置为物理内存的50%)
│   ├── Index Buffer(索引缓冲区)
│   ├── Filter Cache(过滤器缓存)
│   ├── Field Data Cache(字段数据缓存)
│   ├── Query Cache(查询缓存)
│   └── Segment Memory(段内存)
├── OS Page Cache(操作系统页缓存)
└── Doc Values(文档值)

3.2 内存飙升排查

3.2.1 监控指标
bash 复制代码
# 查看JVM内存使用
curl -X GET "localhost:9200/_nodes/stats/jvm?pretty"

# 查看内存池使用情况
curl -X GET "localhost:9200/_nodes/stats/jvm?pretty" | grep -A 20 "memory_pools"

# 查看索引缓冲区使用
curl -X GET "localhost:9200/_nodes/stats/indices?pretty" | grep -A 10 "indexing_pressure"

关键指标

  • heap_used_percent:超过75%需要警惕
  • gc.old_count:老年代GC次数
  • gc.old_time_in_millis:老年代GC耗时
3.2.2 常见原因与解决
原因 症状 解决方案
Field Data过大 聚合查询慢,内存飙升 使用Doc Values代替
索引缓冲区过小 写入频繁触发刷新 调大indices.memory.index_buffer_size
缓存未清理 缓存占用过多内存 调整缓存过期策略
分片过多 每个分片占用内存 减少分片数,使用ILM
大批量查询 一次查询加载过多数据 限制查询大小,使用分页

3.3 GC频繁优化

3.3.1 查看GC日志
bash 复制代码
# 启用GC日志(jvm.options)
-Xlog:gc*,gc+age=trace,safepoint:file=gc.log:utctime,pid,tags:filecount=32,filesize=64m

# 分析GC日志
java -jar gcviewer.jar gc.log
3.3.2 GC优化参数
bash 复制代码
# jvm.options 推荐配置(ES 7.x+)
## 堆内存设置(物理内存的50%,不超过32GB)
-Xms16g
-Xmx16g

## 使用G1垃圾收集器(JDK 9+默认)
-XX:+UseG1GC

## 设置GC停顿时间目标
-XX:MaxGCPauseMillis=200

## 限制G1垃圾收集器的后台并行处理线程数
-XX:ConcGCThreads=4

## 禁用显式GC
-XX:+DisableExplicitGC

## 堆转储
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/elasticsearch/heapdump.hprof
3.3.3 应用层优化
bash 复制代码
# 1. 限制Field Data使用
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "indices.breaker.fielddata.limit": "40%",
    "indices.breaker.total.limit": "95%"
  }
}'

# 2. 使用Doc Values代替Field Data
# 映射设置
{
  "properties": {
    "status": {
      "type": "keyword",
      "doc_values": true,
      "fielddata": false
    }
  }
}

# 3. 清理缓存(紧急情况)
curl -X POST "localhost:9200/your_index/_cache/clear"

3.4 OOM问题根治

3.4.1 OOM原因分析
复制代码
OOM常见原因:
1. 堆内存设置过小
2. 一次查询加载过多数据
3. 聚合查询使用Field Data
4. 索引缓冲区设置过大
5. 分片数量过多
3.4.2 预防措施

1. 合理设置堆内存

bash 复制代码
# 推荐:物理内存的50%,且不超过32GB
# 编辑jvm.options
-Xms16g
-Xmx16g

2. 启用熔断机制

bash 复制代码
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "indices.breaker.total.limit": "95%",
    "indices.breaker.fielddata.limit": "40%",
    "indices.breaker.request.limit": "60%",
    "network.breaker.inflight_requests.limit": "100%"
  }
}'

3. 限制查询内存

json 复制代码
{
  "query": {
    "match_all": {}
  },
  "terminate_after": 10000  // 限制单分片返回文档数
}

4. 使用索引生命周期管理(ILM)

bash 复制代码
# 创建ILM策略
curl -X PUT "localhost:9200/_ilm/policy/logs_policy" -H 'Content-Type: application/json' -d'
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "1GB",
            "max_docs": 1000000
          }
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}'

四、分片迁移失败、索引损坏、数据丢失修复

4.1 分片迁移失败排查

4.1.1 查看迁移状态
bash 复制代码
# 查看正在进行的分片迁移
curl -X GET "localhost:9200/_cat/recovery?v"

# 查看分片分配详情
curl -X GET "localhost:9200/_cluster/allocation/explain?pretty"

# 查看挂起的任务
curl -X GET "localhost:9200/_cluster/pending_tasks?pretty"
4.1.2 迁移失败原因与解决
原因 错误信息 解决方案
磁盘空间不足 NO(disk usage exceeded flood-stage watermark) 清理磁盘或增加节点
节点离线 NO(node left cluster) 恢复节点或重新分配
分片损坏 CORRUPTED_FILE 从副本恢复或重建索引
网络问题 RECOVERY_FAILED 检查网络,重试迁移
并发迁移过多 too many recoveries 限制并发恢复数
bash 复制代码
# 限制分片恢复速度
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "indices.recovery.max_bytes_per_sec": "100mb",
    "cluster.routing.allocation.node_concurrent_recoveries": 2
  }
}'

# 手动取消迁移(紧急情况)
curl -X POST "localhost:9200/_cluster/reroute" -H 'Content-Type: application/json' -d'
{
  "commands": [
    {
      "cancel": {
        "index": "your_index",
        "shard": 0,
        "node": "source_node"
      }
    }
  ]
}'

4.2 索引损坏修复

4.2.1 检测索引损坏
bash 复制代码
# 使用Lucene检查工具
java -cp lucene-core-*.jar org.apache.lucene.index.CheckIndex \
  /path/to/elasticsearch/data/nodes/0/indices/{index_uuid}/{shard_id}/index

# 使用ES内置API
curl -X POST "localhost:9200/your_index/_flush/synced"
curl -X POST "localhost:9200/your_index/_force_merge?max_num_segments=1"
4.2.2 修复方案

方案1:从副本恢复

bash 复制代码
# 关闭索引
curl -X POST "localhost:9200/your_index/_close"

# 分配空主分片(数据会丢失,慎用)
curl -X POST "localhost:9200/_cluster/reroute" -H 'Content-Type: application/json' -d'
{
  "commands": [
    {
      "allocate_empty_primary": {
        "index": "your_index",
        "shard": 0,
        "node": "target_node",
        "accept_data_loss": true
      }
    }
  ]
}'

# 重新打开索引
curl -X POST "localhost:9200/your_index/_open"

方案2:重建索引

bash 复制代码
# 从快照恢复
curl -X POST "localhost:9200/_snapshot/backup_repo/snapshot_1/_restore" -H 'Content-Type: application/json' -d'
{
  "indices": "your_index",
  "ignore_unavailable": true,
  "include_global_state": false
}'

# 从原始数据源重新导入
# (根据具体情况执行)

4.3 数据丢失修复

4.3.1 预防数据丢失
bash 复制代码
# 1. 配置副本数(至少1个副本)
curl -X PUT "localhost:9200/your_index/_settings" -H 'Content-Type: application/json' -d'
{
  "index": {
    "number_of_replicas": 1
  }
}'

# 2. 启用快照备份
curl -X PUT "localhost:9200/_snapshot/backup_repo" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/mnt/backups/elasticsearch",
    "compress": true
  }
}'

# 创建快照
curl -X PUT "localhost:9200/_snapshot/backup_repo/snapshot_1?wait_for_completion=true" -H 'Content-Type: application/json' -d'
{
  "indices": "your_index",
  "ignore_unavailable": true,
  "include_global_state": false
}'
4.3.2 数据恢复流程
复制代码
数据丢失
    ↓
确认丢失范围(索引、分片、时间段)
    ↓
检查是否有副本
    ↓ 是
从副本恢复
    ↓ 否
检查是否有快照
    ↓ 是
从快照恢复
    ↓ 否
从原始数据源重新导入
    ↓
验证数据完整性

从快照恢复示例

bash 复制代码
# 查看可用快照
curl -X GET "localhost:9200/_snapshot/backup_repo/_all?pretty"

# 恢复快照(可以恢复到新索引)
curl -X POST "localhost:9200/_snapshot/backup_repo/snapshot_1/_restore" -H 'Content-Type: application/json' -d'
{
  "indices": "your_index",
  "ignore_unavailable": true,
  "include_global_state": false,
  "rename_pattern": "(.+)",
  "rename_replacement": "restored_$1"
}'

五、集群压力突增、流量峰值容错方案

5.1 压力突增预警机制

5.1.1 关键监控指标
指标类别 监控项 告警阈值 处理策略
CPU os.cpu.percent >80% 限流、扩容
内存 jvm.mem.heap_used_percent >75% 优化查询、增加节点
磁盘 disk.available <15% 清理数据、增加磁盘
写入 indices.indexing.index_total 突增2倍 限流、批量优化
查询 indices.search.query_total 突增2倍 缓存、限流
延迟 indices.search.query_time_in_millis >5000ms 优化查询
5.1.2 监控配置示例
bash 复制代码
# 使用ElasticSearch自带的监控
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "xpack.monitoring.collection.enabled": true,
    "xpack.monitoring.collection.interval": "10s"
  }
}'

# 配置Watcher告警(商业版功能)
curl -X PUT "localhost:9200/_watcher/watch/cluster_health_watch" -H 'Content-Type: application/json' -d'
{
  "trigger": {
    "schedule": { "interval": "30s" }
  },
  "input": {
    "http": {
      "request": {
        "host": "localhost",
        "port": 9200,
        "path": "/_cluster/health"
      }
    }
  },
  "condition": {
    "compare": {
      "ctx.payload.status": { "eq": "red" }
    }
  },
  "actions": {
    "send_email": {
      "email": {
        "to": "admin@example.com",
        "subject": "集群状态红色告警",
        "body": "集群状态变为红色,请立即处理!"
      }
    }
  }
}'

5.2 流量控制策略

5.2.1 限流配置
bash 复制代码
# 1. 限制索引写入速度
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "indices.store.throttle.type": "merge",
    "indices.store.throttle.max_bytes_per_sec": "100mb"
  }
}'

# 2. 限制查询并发
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "thread_pool.search.size": 13,
    "thread_pool.search.queue_size": 1000
  }
}'

# 3. 使用断路器限制内存使用
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "indices.breaker.total.limit": "95%",
    "indices.breaker.fielddata.limit": "40%"
  }
}'
5.2.2 客户端限流

Java客户端示例

java 复制代码
// 使用RateLimiter限制写入速度
RateLimiter rateLimiter = RateLimiter.create(1000.0); // 每秒1000次

public void bulkIndex(List<Document> documents) {
    if (!rateLimiter.tryAcquire()) {
        Thread.sleep(100); // 等待后再试
    }
    // 执行批量写入
}

Nginx限流示例

nginx 复制代码
# 限制每秒请求数
limit_req_zone $binary_remote_addr zone=es_zone:10m rate=100r/s;

location / {
    limit_req zone=es_zone burst=200 nodelay;
    proxy_pass http://elasticsearch:9200;
}

5.3 弹性扩容方案

5.3.1 自动扩容策略
bash 复制代码
# 使用Kubernetes HPA(Horizontal Pod Autoscaler)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: elasticsearch-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: StatefulSet
    name: elasticsearch
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 75
5.3.2 冷热数据分离
bash 复制代码
# 1. 标记节点属性
# elasticsearch.yml
node.attr.data: hot  # 热节点
node.attr.data: warm # 温节点
node.attr.data: cold # 冷节点

# 2. 创建索引时指定分配
curl -X PUT "localhost:9200/logs-2024-01-01" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "index.routing.allocation.require.data": "hot"
  }
}'

# 3. 使用ILM自动迁移
curl -X PUT "localhost:9200/_ilm/policy/logs_policy" -H 'Content-Type: application/json' -d'
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": { "max_size": "1gb" },
          "set_priority": { "priority": 100 }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "allocate": {
            "number_of_replicas": 0,
            "require": { "data": "warm" }
          },
          "set_priority": { "priority": 50 }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "allocate": {
            "number_of_replicas": 0,
            "require": { "data": "cold" }
          },
          "set_priority": { "priority": 0 }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}'

5.4 降级与容错

5.4.1 查询降级策略
json 复制代码
{
  "query": {
    "match": { "title": "elasticsearch" }
  },
  "timeout": "5s",           // 查询超时时间
  "terminate_after": 10000,  // 单分片最大返回数
  "preference": "_local"     // 优先查询本地分片
}
5.4.2 写入降级策略
bash 复制代码
# 1. 异步写入(不等待刷新)
curl -X PUT "localhost:9200/logs/_doc/1?refresh=false" -H 'Content-Type: application/json' -d'
{
  "message": "log message"
}'

# 2. 批量写入(提高吞吐量)
# 见2.2.1节

# 3. 写入队列监控
curl -X GET "localhost:9200/_nodes/stats/thread_pool?pretty" | grep -A 10 "write"
5.4.3 熔断与降级
bash 复制代码
# 1. 启用熔断器
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "indices.breaker.total.limit": "95%",
    "indices.breaker.fielddata.limit": "40%",
    "indices.breaker.request.limit": "60%"
  }
}'

# 2. 查询降级(返回部分结果)
{
  "query": { "match_all": {} },
  "allow_partial_search_results": true
}

5.5 应急预案

5.5.1 应急预案清单
场景 应急措施 恢复步骤
集群红色 1. 检查节点状态 2. 重启故障节点 3. 手动分配分片 1. 确认数据完整 2. 恢复副本设置 3. 验证集群健康
写入拥塞 1. 启用限流 2. 扩容节点 3. 优化批量写入 1. 取消限流 2. 监控写入延迟 3. 调整批量大小
查询超时 1. 终止慢查询 2. 启用缓存 3. 扩容节点 1. 分析慢查询日志 2. 优化查询语句 3. 增加内存
OOM 1. 重启节点 2. 减少分片数 3. 调整堆内存 1. 分析OOM原因 2. 优化查询 3. 调整JVM参数
5.5.2 自动化运维脚本
bash 复制代码
#!/bin/bash
# es_emergency_response.sh - ES应急响应的自动化脚本

CLUSTER_URL="localhost:9200"
ALERT_LOG="/var/log/elasticsearch/alert.log"

# 检查集群健康状态
check_cluster_health() {
    status=$(curl -s "$CLUSTER_URL/_cluster/health" | jq -r '.status')
    if [ "$status" == "red" ]; then
        echo "$(date): 集群状态红色,启动应急流程" >> $ALERT_LOG
        handle_red_status
    elif [ "$status" == "yellow" ]; then
        echo "$(date): 集群状态黄色,检查未分配分片" >> $ALERT_LOG
        handle_yellow_status
    fi
}

# 处理红色状态
handle_red_status() {
    # 获取问题索引
    red_indices=$(curl -s "$CLUSTER_URL/_cat/indices?v&health=red" | awk 'NR>1 {print $3}')
    
    for index in $red_indices; do
        echo "处理红色索引: $index"
        # 检查是否有可用副本
        replicas=$(curl -s "$CLUSTER_URL/_cat/shards/$index?v" | grep "r" | grep "STARTED" | wc -l)
        if [ $replicas -gt 0 ]; then
            echo "索引 $index 有可用副本,等待自动恢复"
        else
            echo "索引 $index 无可用副本,需要从快照恢复"
            # 这里可以添加自动恢复逻辑
        fi
    done
}

# 处理黄色状态
handle_yellow_status() {
    # 获取未分配分片原因
    curl -s "$CLUSTER_URL/_cluster/allocation/explain?pretty" >> $ALERT_LOG
    
    # 检查磁盘空间
    disk_usage=$(curl -s "$CLUSTER_URL/_cat/allocation?v" | awk 'NR>1 {print $5}' | sed 's/%//')
    if [ $disk_usage -gt 85 ]; then
        echo "$(date): 磁盘空间不足,启动清理流程" >> $ALERT_LOG
        clean_old_indices
    fi
}

# 清理旧索引
clean_old_indices() {
    # 删除超过90天的索引(根据实际情况调整)
    old_indices=$(curl -s "$CLUSTER_URL/_cat/indices?v" | awk '$3 ~ /logs-/ && $4 < "2023-10-01" {print $3}')
    for index in $old_indices; do
        echo "删除旧索引: $index"
        curl -X DELETE "$CLUSTER_URL/$index"
    done
}

# 主循环
main() {
    while true; do
        check_cluster_health
        sleep 60  # 每分钟检查一次
    done
}

main

总结

本章详细讲解了ElasticSearch生产环境中的五大类常见故障及其解决方案:

  1. 集群状态异常:通过系统性排查流程,快速定位红色/黄色状态原因并恢复
  2. 性能瓶颈:从写入、查询两方面入手,提供具体的优化技巧和配置建议
  3. 内存问题:深入分析JVM内存结构,提供GC优化和OOM预防方案
  4. 数据完整性:建立分片迁移、索引修复、数据恢复的全流程预案
  5. 高可用架构:通过监控、限流、扩容、降级等手段,构建流量峰值容错能力

最佳实践建议

  • 建立完善的监控告警体系,防患于未然
  • 定期进行快照备份,确保数据安全
  • 使用ILM实现索引生命周期自动化管理
  • 编写应急预案并定期演练,提升故障恢复能力

下一章预告:第16章将深入讲解ElasticSearch安全认证与权限控制,包括X-Pack Security配置、角色权限管理、审计日志等内容,帮助你构建企业级安全集群。


作者注:本文所有代码示例均在ElasticSearch 7.x/8.x版本测试通过,部分配置在5.x/6.x版本中可能有所不同,请根据实际情况调整。