第十章:性能优化

学习 Elasticsearch 性能优化技巧,包括索引优化、查询优化、JVM 调优和硬件选择。

最后更新: 2024-01-15
页面目录

第十章:性能优化

10.1 性能优化概述

10.1.1 性能指标

指标 说明 目标
索引吞吐量 写入文档数/秒 根据数据量评估
查询延迟 P50/P99 响应时间 <100ms / <500ms
资源使用 CPU/内存/磁盘 IO <80% 持续使用

10.1.2 优化方向

性能优化
├── 硬件层面
│   ├── CPU 选择
│   ├── 内存配置
│   └── SSD 存储
├── 索引层面
│   ├── 分片策略
│   ├── 映射优化
│   └── 分词选择
├── 查询层面
│   ├── 查询缓存
│   ├── 过滤器优化
│   └── 聚合优化
└── JVM 层面
    ├── 堆内存配置
    └── GC 调优

10.2 分片优化

10.2.1 分片数量规划

# 分片公式参考
# 建议分片数 = 数据总量 / 单分片大小
# 单分片建议大小: 30-50GB

# 示例:100GB 数据
# 100GB / 40GB = 2.5 → 3 个主分片

PUT /my-large-index
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  }
}

10.2.2 分片过多问题

问题 原因 解决方案
内存压力 每个分片占用堆内存 减少分片数
延迟增加 协调节点合并结果耗时 增加 Coordinating 节点
元数据大 集群状态更新变慢 定期清理旧索引

10.2.3 分片分配策略

PUT /my-index/_settings
{
  "index.routing.allocation.include._tier_preference": "data_hot"
}

10.3 索引优化

10.3.1 刷新间隔

# 批量写入时禁用刷新
PUT /my-index/_settings
{
  "index": {
    "refresh_interval": "-1"
  }
}

# 恢复正常
PUT /my-index/_settings
{
  "index": {
    "refresh_interval": "1s"
  }
}

10.3.2 副本配置

# 导入时禁用副本
PUT /my-index/_settings
{
  "index": {
    "number_of_replicas": 0
  }
}

# 导入完成后启用副本
PUT /my-index/_settings
{
  "index": {
    "number_of_replicas": 1
  }
}

10.3.3 合并段

# 强制合并(减少段数量)
POST /my-index/_forcemerge
{
  "max_num_segments": 1,
  "only_expunge_deletes": true
}

10.3.4 索引压缩

# 使用 source_mode 减少存储
PUT /my-index
{
  "settings": {
    "index.codec": "best_compression"
  }
}

10.4 查询优化

10.4.1 使用 filter 替代 query

# 不好的写法 - 每次计算分数
GET /products/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "iPhone" } }
      ],
      "should": [
        { "term": { "is_featured": true } }
      ]
    }
  }
}

# 好的写法 - filter 不计算分数
GET /products/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "iPhone" } }
      ],
      "filter": [
        { "term": { "is_active": true } },
        { "range": { "price": { "gte": 1000 } } }
      ]
    }
  }
}

10.4.2 避免通配符开头

# 差 - 前缀通配符无法使用索引
GET /products/_search
{
  "query": {
    "wildcard": { "name": "*iPhone*" }
  }
}

# 好 - 使用正则表达式或全文搜索
GET /products/_search
{
  "query": {
    "match": { "name": "iPhone" }
  }
}

10.4.3 分页优化

# 深度分页问题
# 使用 search_after 替代 from/size

# 第一页
GET /products/_search
{
  "size": 10,
  "sort": [
    { "price": "asc" },
    { "_id": "asc" }
  ]
}

# 下一页 - 使用最后一页的最后一条排序值
GET /products/_search
{
  "size": 10,
  "sort": [
    { "price": "asc" },
    { "_id": "asc" }
  ],
  "search_after": [9999, "product_123"]
}

10.4.4 路由优化

# 使用路由直接定位分片
GET /products/_search?routing=user-123
{
  "query": {
    "term": { "user_id": "user-123" }
  }
}

10.4.5 字段截取

# 只获取需要的字段
GET /products/_search
{
  "_source": ["name", "price", "category"],
  "query": {
    "match": { "name": "iPhone" }
  }
}

# 排除大字段
GET /products/_search
{
  "_source": {
    "excludes": ["description", "content"]
  }
}

10.5 聚合优化

10.5.1 限制桶数量

# 限制 terms 聚合返回数量
GET /products/_search
{
  "size": 0,
  "aggs": {
    "top_brands": {
      "terms": {
        "field": "brand",
        "size": 10,
        "shard_size": 50
      }
    }
  }
}

10.5.2 缓存优化

# 启用查询缓存
GET /products/_search
{
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        { "term": { "category": "electronics" } }
      ]
    }
  },
  "aggs": {
    "avg_price": { "avg": { "field": "price" } }
  }
}

10.5.3 近似聚合

# 使用 cardinality 近似计算
GET /products/_search
{
  "size": 0,
  "aggs": {
    "unique_users": {
      "cardinality": {
        "field": "user_id",
        "precision_threshold": 100
      }
    }
  }
}

10.6 JVM 优化

10.6.1 堆内存配置

# jvm.options

# 建议堆大小为物理内存的 50%,最大不超过 32GB
-Xms8g
-Xmx8g

# 使用 G1 GC
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1ReservePercent=25

10.6.2 内存锁定

# elasticsearch.yml
bootstrap.memory_lock: true

# 系统配置
sudo systemctl edit elasticsearch
# 添加:
[Service]
LimitMEMLOCK=infinity

10.6.3 堆外内存

# 使用堆外内存存储索引
path.data: /data/elasticsearch/data

10.7 系统优化

10.7.1 文件描述符

# 查看当前限制
ulimit -n

# 临时修改
ulimit -n 65535

# 永久修改
sudo vim /etc/security/limits.conf
elasticsearch  -  nofile  65535

10.7.2 虚拟内存

# 增加 mmap 数量
sudo sysctl -w vm.max_map_count=262144

# 永久配置
sudo vim /etc/sysctl.conf
vm.max_map_count=262144

10.7.3 网络优化

# 调整网络缓冲区
sudo sysctl -w net.core.rmem_max=1g
sudo sysctl -w net.core.wmem_max=1g

10.8 监控与诊断

10.8.1 节点统计

# 节点统计
GET /_nodes/stats

# 索引统计
GET /_stats

# 分片统计
GET /_cat/shards?v&h=index,shard,prirep,state,store,docs,size

10.8.2 慢查询日志

# elasticsearch.yml
index.search.slowlog.threshold.query.warn: 10s
index.search.slowlog.threshold.query.info: 2s
index.search.slowlog.threshold.query.debug: 1s
index.search.slowlog.threshold.query.trace: 500ms

index.indexing.slowlog.threshold.index.warn: 10s
index.indexing.slowlog.threshold.index.info: 5s
index.indexing.slowlog.threshold.index.debug: 2s
index.indexing.slowlog.threshold.index.trace: 500ms

10.8.3 热线程

GET /_nodes/hot_threads
GET /_nodes/hot_threads?threads=5

10.9 优化检查清单

□ 分片数量合理(30-50GB/分片)
□ 副本数量正确配置
□ 使用 filter 而非 query
□ 禁用不需要的字段
□ 使用 keyword 而非 text 进行精确匹配
□ 合理设置 refresh_interval
□ 批量操作使用 Bulk API
□ 深度分页使用 search_after
□ 启用查询缓存
□ JVM 堆内存配置正确
□ 系统文件描述符充足
□ 使用 SSD 存储
□ 堆外内存充足

10.10 总结

本章介绍了 Elasticsearch 性能优化的多个方面,包括分片策略、索引优化、查询优化和系统配置。性能调优是一个持续的过程,需要根据实际业务场景和监控数据不断调整。