性能优化
本章介绍 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
小结
本章我们学习了:
- 写入优化(批量写入、调整刷新间隔)
- 查询优化(使用 filter、避免深度分页)
- 索引优化(分片数量、生命周期管理)
- JVM 优化(堆内存、垃圾收集器)
- 系统优化(文件描述符、禁用 swap)
- 监控指标和问题排查
练习
- 使用 Bulk API 批量导入数据并对比性能
- 对比 filter 和 query 的查询性能
- 配置慢查询日志并分析
- 使用 Profile API 分析查询性能