俗话说得好,人在河边走,哪有不湿鞋
咱们的Es宝贝被越来越多的人看到,难免也有些是非,今天咱们不惧绯闻,大胆的来说一说吧
1. OOM Killer 突然干掉 ES 进程
有没有这种经历:我大手一挥直接给了 32G 内存,这么富裕的仗,ES 应该很稳",结果半夜收到告警:节点离线,服务中断。( 虚拟场景,不要当真哦,还是要结合业务**)**
# elasticsearch.yml
-Xms32g
-Xmx32g # ← 危险!超过 32G左右(看版本) 指针压缩失效,建议30
-
💥 现象:节点无故下线killed,也没有日志OOM,咱们上命令:dmesg -T | grep -i "killed process"
[Thu Feb 6 03:14:22 2025] Out of memory: Kill process 12345 (java) score 899 or sacrifice child
[Thu Feb 6 03:14:22 2025] Killed process 12345 (java), UID 1000, total-vm: 65000000kB, anon-rss: 31000000kB#JVM OOM:Java 抛异常,ES 进程还在(可重启)
#Linux OOM Killer:直接 kill -9 进程,无日志、无预警、无商量! -
ES 的内存 = JVM 堆 + 非堆(Off-Heap);或 fielddata/聚合吃光堆内存;
| 内存区域 | 用途 | 是否受 -Xmx 控制 |
|---|---|---|
| JVM Heap | fielddata、聚合、查询上下文 | ✅ 是 |
| Lucene Off-Heap | 倒排索引(FST)、Doc Values、缓存 | ❌ 否!由 OS Page Cache 管理 |
| Netty Direct Memory | 网络缓冲区(Bulk 请求) | ❌ 否!默认无上限 |
所以,有点眉目了吧,你以为-Xmx32g,就是只用了32g?太单纯、后台的内幕你知道不?lucene会把倒排索引、docValues加载到PageCache (可能占20-50g),而且Netty缓冲区还要再吃个几G,(咱们Netty默默无闻就可以被无视嘛!第一个不答应)
总内存 = 32(堆) + 30G(非堆) = 62G > 机器 64G这无疑是老鼠和猫玩游戏------不要命了
所以咱们Liunx 看不下去了,触发OOM Killer ,优先把占用内存最大的进程ES给die了
#JVM OOM:Java 抛异常,ES 进程还在(可重启)
#Linux OOM Killer:直接 kill -9 进程,无日志、无预警、无商量!
💡 更隐蔽的问题 :藏了一把大的!JVM 堆设太大(如 -Xmx64g),导致 指针压缩失效,内存翻倍
当 -Xmx 约32GB 时,JVM 指针压缩(Compressed Oops)失效 ,导致 实际堆内存膨胀 20%+(32G 堆 ≈ 38G 物理内存):每个对象引用从4字节膨胀成8字节,堆内存增加 gc压力山大
而且下面如果不注意也会很有隐患:自古大哥不是好当的,自己人可能就keng个够呛 🤣
1、容许text字段加载fielddate
PUT /my_index/_mapping
{ "properties": { "content": { "type": "text", "fielddata": true } } }
这样导致什么呢?所有的唯一值加载到jvm堆 ,都想占据一席之地;再者说1亿条不同日志,堆不直接爆,天理难容!
2、不做熔断限制,这妹妹就有点糊涂了,明显涉世未深,这🉑️不能不调,原来的不老实呢
# 默认熔断器只限制 70% 堆,但非堆内存不受控
indices.breaker.request.limit: 60% # ← 很多人没调
正确姿势:💃💃💃 ✅ 必须配置,否则大聚合会吃光堆
# elasticsearch.yml
indices.breaker.request.limit: 40% # 单请求最多用 40% 堆
indices.breaker.fielddata.limit: 20% # fielddata 最多 20%
network.breaker.inflight.requests.limit: 50% # 网络缓冲
- 🛠️ 解决:
- JVM 堆 ≤ 31GB(推荐 26--30GB)-Xms30g -Xmx30g 保证指针压缩生效,这么重要的好东西咱不用那是糊涂,可不能糊涂两次咯;30G 堆 ≈ 32G 物理内存
- 留给Lucene至少50%的物理内存, 不能让大将军无处可去,那么多士兵无家可归!机器是64G,堆就要30G,剩下34G给OsPageCache(lucene用,当然别的也用,共享的)
- 同时不要设swap(bootstrap.memory_lock: true )锁内存 防 swap!
- 监控 一查看熔断器状态
GET /_nodes/stats/breaker ;二查看fileddata使用情况GET /_stats/fielddata?fields=*,俗话说小心驶得万年船,多加注意 健康dmesg或/var/log/messages中的killed process java,一双眼睛时刻盯着案发现场,当然也可以用filebeat+es自身做日志分析,一直盯着也难受是不是- 实在不行报警吧:当
fielddata memory usage > 10GB→ 告警;当breaker tripped count > 0→ 紧急告警 - 心要用在关键时刻,不屈小节:聚合用
.keyword,禁用 text 的 fielddata(8+默认禁了)
// 正确 mapping
"message": {
"type": "text",
"fields": {
"keyword": { "type": "keyword", "ignore_above": 256 }
}
}
// 聚合查 message.keyword,不碰堆内存
😅 "你给 ES 32G 内存,它还你一个 OOM ------ 这叫好心当了驴肝肺"
附赠一个咱家的配置吧:
Elasticsearch 8.x 生产环境推荐的内存安全配置
1、config/jvm.options(JVM 层)
##################################################################
## JVM Heap Size (关键!)
## 原则:堆 ≤ 30GB,且 ≤ 物理内存的 50%
## 示例:64GB 机器 → -Xms30g -Xmx30g
##################################################################
-Xms30g
-Xmx30g
##################################################################
## GC 策略(ES 8.x 默认 G1GC,无需改)
## 不要手动指定 -XX:+UseG1GC(8.x 已默认)
##################################################################
# -XX:+UseG1GC # ← ES 8.x 已默认,无需重复
##################################################################
## 内存保护(防止指针膨胀 & OOM)
##################################################################
# 启用压缩普通对象指针(Compressed Oops)------堆 ≤32GB 时自动生效
-XX:+UseCompressedOops
# 禁用显式 GC(防止 System.gc() 触发 Full GC)
-XX:+DisableExplicitGC
# G1GC 优化(官方推荐)
-XX:G1HeapRegionSize=16m
-XX:InitiatingHeapOccupancyPercent=30
##################################################################
## 监控与诊断(可选但推荐)
##################################################################
# OOM 时生成 heap dump(谨慎!可能很大)
# -XX:+HeapDumpOnOutOfMemoryError
# -XX:HeapDumpPath=/var/lib/elasticsearch
# GC 日志(ES 8.x 默认输出到 logs/gc.log,无需额外配置)
- 如果机器内存 < 64GB,请按比例调整(如 32GB 机器 →
-Xms16g -Xmx16g)对半吧
2、config/elasticsearch.yml(ES 应用层)
# ======================== 内存与安全 ========================
# 锁定内存,防止 swap(必须配合系统设置)
bootstrap.memory_lock: true
# 熔断器:防止单个请求吃光内存(关键!)
indices.breaker.request.limit: 40% # 单次请求最多用 40% 堆
indices.breaker.fielddata.limit: 20% # fielddata 最多 20% 堆
indices.breaker.total.limit: 70% # 所有 breaker 总和上限
# 网络缓冲区熔断(防 Bulk 压垮)
network.breaker.inflight.requests.limit: 50%
# ======================== fielddata 防护 ========================
# 全局禁止 text 字段加载 fielddata(ES 8.x 默认已禁,双重保险)
index.fielddata.cache.expire: 0s
index.fielddata.cache.size: 0
# ======================== 其他关键配置 ========================
# 线程池队列大小(防拒绝)
thread_pool:
write:
queue_size: 2000
search:
queue_size: 2000
# 磁盘水位(防只读)
cluster.routing.allocation.disk.watermark.low: 85%
cluster.routing.allocation.disk.watermark.high: 90%
cluster.routing.allocation.disk.watermark.flood_stage: 95%
# 安全(ES 8.x 默认启用)
xpack.security.enabled: true
xpack.monitoring.collection.enabled: true
3、其他
1、关闭SWAP
-
临时 sudo swapoff -a
-
永久:注释 /etc/fstab 中的 swap 行
2、增大文件描符 & 内存锁限制:其他场景也有用哦
编辑 /etc/security/limits.conf:英文的重要性体现一下下
- elasticsearch soft nofile 65536
- elasticsearch hard nofile 65536
- elasticsearch soft memlock unlimited
- elasticsearch hard memlock unlimited
3、验证memory lock,就是看看日志有没有,此刻安排眼线的重要性略显一二
[INFO][o.e.b.BootstrapChecks] [node-1] bound or publishing to a non-loopback address,
enforcing bootstrap checks
[INFO][o.e.b.BootstrapChecks] [node-1] memory locking requested for elasticsearch process but not enabled
看看memory locking ... not enabled 没配好🤣