第十章:性能优化
学习 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 性能优化的多个方面,包括分片策略、索引优化、查询优化和系统配置。性能调优是一个持续的过程,需要根据实际业务场景和监控数据不断调整。