跳到主要内容

性能优化

本章介绍 Elasticsearch 的性能优化策略。

写入优化

批量写入

# 使用 Bulk API 批量写入
POST /_bulk
{"index": {"_index": "articles", "_id": "1"}}
{"title": "文章1", "content": "内容1"}
{"index": {"_index": "articles", "_id": "2"}}
{"title": "文章2", "content": "内容2"}

建议

  • 批量大小:5-15 MB 或 1000-5000 个文档
  • 并发批量写入,但不要过多

调整刷新间隔

# 批量导入前禁用刷新
PUT /articles/_settings
{
"index": {
"refresh_interval": "-1",
"number_of_replicas": 0
}
}

# 批量导入后恢复
PUT /articles/_settings
{
"index": {
"refresh_interval": "1s",
"number_of_replicas": 1
}
}

# 手动刷新
POST /articles/_refresh

调整事务日志

PUT /articles/_settings
{
"index": {
"translog": {
"durability": "async", # 异步刷新
"sync_interval": "5s", # 同步间隔
"flush_threshold_size": "512mb" # 刷新阈值
}
}
}

强制合并段

# 导入完成后合并段
POST /articles/_forcemerge?max_num_segments=1

查询优化

使用 Filter 代替 Query

# 不推荐:使用 must(会计算分数)
GET /articles/_search
{
"query": {
"bool": {
"must": [
{ "term": { "status": "published" } }
]
}
}
}

# 推荐:使用 filter(会缓存结果)
GET /articles/_search
{
"query": {
"bool": {
"filter": [
{ "term": { "status": "published" } }
]
}
}
}

使用路由

# 写入时指定路由
PUT /articles/_doc/1?routing=user_123
{
"title": "文章标题",
"user_id": "user_123"
}

# 查询时指定路由(只搜索特定分片)
GET /articles/_search?routing=user_123
{
"query": {
"match_all": {}
}
}

只返回需要的字段

# 不推荐:返回所有字段
GET /articles/_search
{
"query": { "match_all": {} }
}

# 推荐:只返回需要的字段
GET /articles/_search
{
"query": { "match_all": {} },
"_source": ["title", "author"]
}

# 或使用 filter_path
GET /articles/_search?filter_path=hits.hits._id,hits.hits._source.title

避免深度分页

# 不推荐:from + size 超过 10000
GET /articles/_search
{
"from": 10000,
"size": 10
}

# 推荐:使用 search_after
GET /articles/_search
{
"size": 10,
"sort": [
{ "created_at": "desc" },
{ "_id": "desc" }
],
"search_after": ["2024-01-15", "abc123"]
}

索引优化

合理设置分片数量

┌─────────────────────────────────────────────────────────────┐
│ 分片数量建议 │
├─────────────────────────────────────────────────────────────┤
│ 数据量 分片数量 单分片大小 │
│ < 1 GB 1 < 1 GB │
│ 1 GB - 10 GB 1-2 1-5 GB │
│ 10 GB - 100 GB 3-5 10-30 GB │
│ 100 GB - 1 TB 10-20 10-50 GB │
│ > 1 TB 按需增加 10-50 GB │
└─────────────────────────────────────────────────────────────┘

原则

  • 每个分片大小控制在 10-50GB
  • 分片数量 = 数据量 / 期望分片大小
  • 考虑未来的数据增长

使用索引别名

# 创建别名
POST /_aliases
{
"actions": [
{ "add": { "index": "articles_v2", "alias": "articles" } }
]
}

# 零停机切换索引
POST /_aliases
{
"actions": [
{ "remove": { "index": "articles_v1", "alias": "articles" } },
{ "add": { "index": "articles_v2", "alias": "articles" } }
]
}

使用索引生命周期管理

# 创建生命周期策略
PUT /_ilm/policy/articles_policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_size": "50gb",
"max_age": "30d"
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"shrink": { "number_of_shards": 1 },
"forcemerge": { "max_num_segments": 1 }
}
},
"delete": {
"min_age": "90d",
"actions": {
"delete": {}
}
}
}
}
}

JVM 优化

堆内存设置

# jvm.options
-Xms4g
-Xmx4g

原则

  • 堆内存设置为物理内存的 50%,不超过 32GB
  • Xms 和 Xmx 设置相同值
  • 超过 32GB 会禁用压缩指针

垃圾收集器

# jvm.options(JDK 11+ 默认 G1GC)
-XX:+UseG1GC
-XX:G1HeapRegionSize=16m
-XX:InitiatingHeapOccupancyPercent=30

系统优化

文件描述符

# /etc/security/limits.conf
elasticsearch soft nofile 65536
elasticsearch hard nofile 65536

虚拟内存

# /etc/sysctl.conf
vm.max_map_count=262144

# 应用配置
sysctl -p

禁用 Swap

# 临时禁用
swapoff -a

# 永久禁用(/etc/fstab)
# 注释掉 swap 行

或锁定内存

# elasticsearch.yml
bootstrap.memory_lock: true

监控指标

关键指标

# JVM 堆使用率
GET /_nodes/stats?filter_path=nodes.*.jvm.mem.heap_used_percent

# 索引速度
GET /_nodes/stats?filter_path=nodes.*.indices.indexing.index_total

# 搜索延迟
GET /_nodes/stats?filter_path=nodes.*.indices.search.query_time_in_millis

# 线程池状态
GET /_nodes/stats?filter_path=nodes.*.thread_pool

# GC 统计
GET /_nodes/stats?filter_path=nodes.*.jvm.gc

集群健康

# 集群状态
GET /_cluster/health

# 未分配分片
GET /_cat/shards?v&s=state

# 磁盘使用
GET /_cat/allocation?v

性能问题排查

使用 Profile API

GET /articles/_search
{
"profile": true,
"query": {
"match": { "title": "Elasticsearch" }
}
}

慢查询日志

# elasticsearch.yml
index.search.slowlog.threshold.query.warn: 10s
index.search.slowlog.threshold.query.info: 5s
index.search.slowlog.threshold.query.debug: 2s

index.indexing.slowlog.threshold.index.warn: 10s
index.indexing.slowlog.threshold.index.info: 5s

节点热点线程

GET /_nodes/hot_threads

小结

本章我们学习了:

  1. 写入优化(批量写入、调整刷新间隔)
  2. 查询优化(使用 filter、避免深度分页)
  3. 索引优化(分片数量、生命周期管理)
  4. JVM 优化(堆内存、垃圾收集器)
  5. 系统优化(文件描述符、禁用 swap)
  6. 监控指标和问题排查

练习

  1. 使用 Bulk API 批量导入数据并对比性能
  2. 对比 filter 和 query 的查询性能
  3. 配置慢查询日志并分析
  4. 使用 Profile API 分析查询性能

参考资源