← 返回文章列表

Elasticsearch 底层原理系列(九):生产实践与性能调优

2020-12-31·6 分钟阅读

前言

将 Elasticsearch 应用于生产环境需要考虑诸多因素:硬件选型、JVM 配置、索引设计、查询优化、监控告警等。本章将总结生产环境的最佳实践和性能调优技巧。

技术亮点

技术点难度面试价值本文覆盖
硬件选型⭐⭐实战价值
JVM 调优⭐⭐⭐⭐高频考点
索引设计⭐⭐⭐⭐高频考点
性能监控⭐⭐⭐实战价值
问题排查⭐⭐⭐⭐实战价值

面试考点

  1. ES 生产环境的硬件应该如何选型?
  2. JVM 堆内存应该如何配置?为什么有 32GB 限制?
  3. 如何设计索引以获得最佳性能?
  4. ES 常见的性能问题有哪些?如何排查?

硬件选型

存储选型

┌─────────────────────────────────────────────────────────────────────────┐
│                        存储选型建议                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  存储类型对比:                                                          │
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  存储类型       随机读写    顺序读写    成本    推荐场景        │   │
│  │  ──────────────────────────────────────────────────────────     │   │
│  │  HDD            低         中         低      冷数据           │   │
│  │  SATA SSD       中         高         中      温数据           │   │
│  │  NVMe SSD       高         极高       高      热数据           │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  推荐:                                                                  │
│  • 热数据:NVMe SSD                                                     │
│  • 温数据:SATA SSD                                                     │
│  • 冷数据:HDD 或对象存储                                               │
│                                                                         │
│  注意事项:                                                              │
│  • 避免使用网络存储(NFS、NAS)                                         │
│  • 禁用 swap                                                            │
│  • 使用 XFS 文件系统                                                    │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

内存配置

┌─────────────────────────────────────────────────────────────────────────┐
│                        内存配置建议                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  JVM 堆内存:                                                            │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  • 堆内存 ≤ 物理内存的 50%                                      │   │
│  │  • 堆内存最大 31GB(压缩指针阈值)                              │   │
│  │  • 设置 Xms = Xmx(避免动态调整)                               │   │
│  │                                                                 │   │
│  │  示例:64GB 物理内存                                            │   │
│  │  • 堆内存:31GB                                                 │   │
│  │  • 留给 OS Cache:33GB                                          │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  为什么有 31GB 限制?                                                    │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  JVM Compressed OOPs(压缩指针):                              │   │
│  │  • < 32GB:使用 32 位指针,内存效率高                           │   │
│  │  > 32GB:使用 64 位指针,内存开销增大                           │   │
│  │                                                                 │   │
│  │  超过 32GB 后:                                                 │   │
│  │  • 有效内存反而可能减少                                         │   │
│  │  • GC 停顿时间增加                                              │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  内存锁定配置:                                                          │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  # elasticsearch.yml                                            │   │
│  │  bootstrap.memory_lock: true                                    │   │
│  │                                                                 │   │
│  │  # 系统配置                                                     │   │
│  │  ulimit -l unlimited                                            │   │
│  │                                                                 │   │
│  │  # 或在 /etc/security/limits.conf                               │   │
│  │  elasticsearch soft memlock unlimited                           │   │
│  │  elasticsearch hard memlock unlimited                           │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

CPU 配置

┌─────────────────────────────────────────────────────────────────────────┐
│                        CPU 配置建议                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  不同角色的 CPU 需求:                                                   │
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  节点角色        CPU 核心    说明                               │   │
│  │  ──────────────────────────────────────────────────────         │   │
│  │  Master          4-8        轻量级,核心数不必太多              │   │
│  │  Data            8-32+      计算、索引、合并都需要 CPU          │   │
│  │  Coordinating    4-16       请求解析、结果聚合                  │   │
│  │  Ingest          8-16       数据预处理                          │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  线程池配置:                                                            │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  # 默认配置通常足够                                             │   │
│  │  thread_pool.search.size: 可用处理器数                          │   │
│  │  thread_pool.search.queue_size: 1000                            │   │
│  │  thread_pool.write.size: 可用处理器数                           │   │
│  │  thread_pool.write.queue_size: 10000                            │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

JVM 调优

JVM 配置

# jvm.options

# 堆内存设置
-Xms31g
-Xmx31g

# GC 配置(ES 8.x 默认使用 G1GC)
-XX:+UseG1GC

# G1GC 调优
-XX:G1HeapRegionSize=16m
-XX:G1ReservePercent=25
-XX:InitiatingHeapOccupancyPercent=30

# 内存溢出时生成堆转储
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/elasticsearch/heapdump.hprof

# GC 日志
-Xlog:gc*,gc+age=trace,gc+heap=debug:file=/var/log/elasticsearch/gc.log:utctime,pid,tags:filecount=32,filesize=64m

GC 监控

┌─────────────────────────────────────────────────────────────────────────┐
│                        GC 监控指标                                       │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  正常指标:                                                              │
│  • GC 频率:< 1 次/秒                                                   │
│  • Young GC 时间:< 50ms                                                │
│  • Full GC 频率:< 1 次/小时                                            │
│  • Full GC 时间:< 1s                                                   │
│                                                                         │
│  异常信号:                                                              │
│  • 频繁 Full GC                                                         │
│  • GC 时间过长                                                          │
│  • 堆内存使用率 > 75%                                                   │
│                                                                         │
│  查看命令:                                                              │
│  GET /_nodes/stats?filter_path=nodes.*.jvm.gc                          │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

索引设计最佳实践

Mapping 设计

┌─────────────────────────────────────────────────────────────────────────┐
│                        Mapping 设计原则                                  │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  1. 禁用不需要的特性                                                    │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  PUT /my_index                                                  │   │
│  │  {                                                              │   │
│  │    "mappings": {                                                │   │
│  │      "properties": {                                            │   │
│  │        "title": {                                               │   │
│  │          "type": "text",                                        │   │
│  │          "norms": false,          // 不需要评分时禁用           │   │
│  │          "index_options": "freqs"  // 不需要位置信息            │   │
│  │        },                                                       │   │
│  │        "status": {                                              │   │
│  │          "type": "keyword",                                     │   │
│  │          "doc_values": true,      // 聚合排序需要               │   │
│  │          "index": false           // 不需要全文搜索             │   │
│  │        },                                                       │   │
│  │        "timestamp": {                                           │   │
│  │          "type": "date",                                        │   │
│  │          "doc_values": true                                     │   │
│  │        }                                                        │   │
│  │      }                                                          │   │
│  │    }                                                            │   │
│  │  }                                                              │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  2. 使用合适的字段类型                                                  │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  • keyword: 精确匹配、聚合、排序                                │   │
│  │  • text: 全文搜索                                               │   │
│  │  • date: 时间字段                                               │   │
│  │  • integer/long: 数值范围查询                                   │   │
│  │  • nested: 嵌套对象数组                                         │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  3. 避免动态映射                                                        │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  PUT /my_index                                                  │   │
│  │  {                                                              │   │
│  │    "mappings": {                                                │   │
│  │      "dynamic": "strict",       // 或 false                    │   │
│  │      "properties": { ... }                                      │   │
│  │    }                                                            │   │
│  │  }                                                              │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

分片规划

┌─────────────────────────────────────────────────────────────────────────┐
│                        分片规划实践                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  分片数量建议:                                                          │
│  • 单分片大小:10GB - 50GB                                              │
│  • 每个节点分片数:≤ 堆内存(GB) × 20                                    │
│                                                                         │
│  副本数量:                                                              │
│  • 生产环境:至少 1 个副本                                              │
│  • 高可用:2 个副本                                                     │
│  • 离线分析:可以 0 副本                                                │
│                                                                         │
│  示例配置:                                                              │
│  PUT /my_index                                                          │
│  {                                                                      │
│    "settings": {                                                        │
│      "number_of_shards": 5,                                             │
│      "number_of_replicas": 1                                            │
│    }                                                                    │
│  }                                                                      │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

索引模板

// 索引模板示例
PUT /_index_template/logs_template
{
  "index_patterns": ["logs-*"],
  "priority": 100,
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "refresh_interval": "30s",
      "index.lifecycle.name": "logs_policy",
      "index.lifecycle.rollover_alias": "logs"
    },
    "mappings": {
      "properties": {
        "@timestamp": { "type": "date" },
        "message": { "type": "text" },
        "level": { "type": "keyword" },
        "service": { "type": "keyword" }
      }
    }
  }
}

查询优化

查询优化技巧

┌─────────────────────────────────────────────────────────────────────────┐
│                        查询优化技巧                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  1. 使用 Filter 代替 Query                                              │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  // 精确匹配使用 Filter(可缓存)                               │   │
│  │  {                                                              │   │
│  │    "query": {                                                   │   │
│  │      "bool": {                                                  │   │
│  │        "must": [                                                │   │
│  │          { "match": { "title": "search" } }                     │   │
│  │        ],                                                       │   │
│  │        "filter": [                                              │   │
│  │          { "term": { "status": "published" } },                 │   │
│  │          { "range": { "date": { "gte": "2023-01-01" } } }       │   │
│  │        ]                                                        │   │
│  │      }                                                          │   │
│  │    }                                                            │   │
│  │  }                                                              │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  2. 限制返回字段                                                        │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  // 只返回需要的字段                                            │   │
│  │  {                                                              │   │
│  │    "_source": ["title", "author", "date"],                      │   │
│  │    "query": { ... }                                             │   │
│  │  }                                                              │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  3. 避免深度分页                                                        │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  // 使用 search_after 代替 from/size                            │   │
│  │  {                                                              │   │
│  │    "size": 100,                                                 │   │
│  │    "query": { ... },                                            │   │
│  │    "sort": [                                                    │   │
│  │      { "date": "desc" },                                        │   │
│  │      { "_id": "asc" }                                           │   │
│  │    ],                                                           │   │
│  │    "search_after": ["2023-08-30", "doc_123"]                    │   │
│  │  }                                                              │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  4. 使用批量操作                                                        │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  // 使用 _bulk 批量写入                                         │   │
│  │  POST /_bulk                                                    │   │
│  │  {"index": {"_index": "my_index", "_id": "1"}}                  │   │
│  │  {"title": "Document 1"}                                        │   │
│  │  {"index": {"_index": "my_index", "_id": "2"}}                  │   │
│  │  {"title": "Document 2"}                                        │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

性能监控

关键监控指标

┌─────────────────────────────────────────────────────────────────────────┐
│                        关键监控指标                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  集群级别:                                                              │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  GET /_cluster/health                                           │   │
│  │  • status: green/yellow/red                                    │   │
│  │  • number_of_nodes                                              │   │
│  │  • number_of_data_nodes                                         │   │
│  │  • active_shards_percent                                        │   │
│  │  • unassigned_shards                                            │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  节点级别:                                                              │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  GET /_nodes/stats                                              │   │
│  │  • jvm.mem.heap_used_percent    < 75%                          │   │
│  │  • jvm.gc收集时间               Young < 50ms, Full < 1s         │   │
│  │  • os.cpu.percent               < 80%                          │   │
│  │  • fs.disk_reads/writes                                        │   │
│  │  • thread_pool 队列积压                                         │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  索引级别:                                                              │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  GET /_stats                                                    │   │
│  │  • indexing.index_total                                         │   │
│  │  • indexing.index_time_in_millis                               │   │
│  │  • search.query_total                                           │   │
│  │  • search.query_time_in_millis                                  │   │
│  │  • segments.count                                               │   │
│  │  • segments.memory_in_bytes                                     │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

常用监控命令

# 集群健康
GET /_cluster/health?pretty

# 节点信息
GET /_cat/nodes?v&h=name,heap.percent,ram.percent,cpu,load_1m,node.role

# 分片分配
GET /_cat/allocation?v

# 索引状态
GET /_cat/indices?v&health=yellow

# 慢查询
GET /_cat/thread_pool?v&h=node_name,name,active,queue,rejected

# 任务状态
GET /_tasks?detailed=true&actions=*search

常见问题排查

高 CPU 使用率

┌─────────────────────────────────────────────────────────────────────────┐
│                    高 CPU 排查                                           │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  可能原因:                                                              │
│  1. 复杂查询(深度聚合、脚本查询)                                      │
│  2. 频繁 GC                                                            │
│  3. 大量索引操作                                                        │
│  4. Segment 合并                                                        │
│                                                                         │
│  排查步骤:                                                              │
│  1. 查看热点线程:GET /_nodes/hot_threads                              │
│  2. 检查慢查询日志                                                      │
│  3. 检查 GC 日志                                                        │
│  4. 检查索引写入速率                                                    │
│                                                                         │
│  解决方案:                                                              │
│  • 优化查询语句                                                          │
│  • 增加缓存                                                              │
│  • 调整线程池                                                            │
│  • 增加节点                                                              │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

高内存使用

┌─────────────────────────────────────────────────────────────────────────┐
│                    高内存排查                                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  可能原因:                                                              │
│  1. 堆内存不足                                                          │
│  2. Field Data 过大                                                    │
│  3. Segment 内存占用                                                    │
│  4. 缓存配置过大                                                        │
│                                                                         │
│  排查步骤:                                                              │
│  1. GET /_nodes/stats?filter_path=nodes.*.jvm                         │
│  2. GET /_nodes/stats?filter_path=nodes.*.indices.segments            │
│  3. GET /_nodes/stats?filter_path=nodes.*.indices.fielddata           │
│                                                                         │
│  解决方案:                                                              │
│  • 增加堆内存(但不超过 31GB)                                          │
│  • 使用 doc_values 代替 fielddata                                      │
│  • 减少 Segment 数量(force merge)                                     │
│  • 调整缓存大小                                                          │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

查询慢

┌─────────────────────────────────────────────────────────────────────────┐
│                    查询慢排查                                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  可能原因:                                                              │
│  1. 查询语句效率低                                                      │
│  2. 索引设计不合理                                                      │
│  3. 分片分布不均                                                        │
│  4. 缓存未命中                                                          │
│                                                                         │
│  排查步骤:                                                              │
│  1. 使用 Profile API 分析查询                                          │
│     GET /my_index/_search?profile=true                                 │
│                                                                         │
│  2. 检查查询缓存命中率                                                  │
│     GET /_stats?filter_path=indices.*.total.query_cache                │
│                                                                         │
│  3. 检查 Segment 数量                                                   │
│     GET /_cat/segments?v                                                │
│                                                                         │
│  解决方案:                                                              │
│  • 优化查询语句(使用 Filter、限制范围)                                │
│  • 添加合适的索引和映射                                                  │
│  • 调整分片数量                                                          │
│  • 增加缓存大小                                                          │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

系列总结

核心知识点回顾

┌─────────────────────────────────────────────────────────────────────────┐
│                        Elasticsearch 核心原理总结                        │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  架构层次:                                                              │
│  Cluster → Node → Index → Shard → Segment → Lucene Index               │
│                                                                         │
│  核心机制:                                                              │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  写入流程:Buffer → Translog → Refresh → Segment → Flush      │   │
│  │  查询流程:Query (找ID) → Fetch (取文档)                        │   │
│  │  评分算法:BM25(TF 饱和 + 文档长度归一化)                      │   │
│  │  存储结构:倒排索引 + Doc Values(列式) + Stored Fields        │   │
│  │  分布式:分片路由 + Master 选举 + 副本同步                       │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  性能优化要点:                                                          │
│  • 硬件:SSD、充足内存、合理 CPU                                        │
│  • JVM:堆内存 ≤ 31GB、禁用 swap                                        │
│  • 索引:合理分片、优化 Mapping、使用模板                               │
│  • 查询:Filter 优先、限制返回、避免深度分页                            │
│  • 监控:集群健康、GC、慢查询                                           │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

面试高频问题

问题核心答案
为什么 ES 搜索快?倒排索引 + FST + 多分片并行
写入流程是什么?Buffer → Refresh(1s) → Segment → Flush
如何避免脑裂?奇数个 Master 节点 + minimum_master_nodes
分片数如何规划?单分片 10-50GB,每节点 ≤ 20分片/GB堆内存
Query vs Filter?Filter 不计算分数、可缓存、更快

参考资料


本系列完。感谢阅读!

分享: