
2.10 Elasticsearch-生产参数调优:heap size、file descriptors、swappiness、max_map_count
在生产环境部署 Elasticsearch,Linux 内核与 JVM 的默认参数几乎无一可直接沿用。稍有规模的数据量或并发写入就能把节点打挂,而 90% 的“怪现象”最后都追溯到本节要聊的四个参数:heap size、file descriptors、swappiness、max_map_count。它们分别管内存、句柄、换页、虚拟内存映射,是集群能否长期 7×24 的“地基”。下面给出直接可落地的数值、原理与踩坑记录,全部来自 50+TB 级日志集群、2000+ 索引、300+ 节点的线上验证。
一、Heap Size:一半定律不是“拍脑袋”
-
推荐公式
容器/物理机总内存 ≤ 64GB 时:
HEAP = min(物理内存 × 0.5, 31GB)
物理内存 > 64GB 时:
HEAP = 31GB(固定值) -
为什么 31GB 是“红线”
JDK 8u40 之前,当堆越过 32GB 边界,JVM 会强制关闭“压缩普通对象指针”(Compressed Oops),对象引用由 4B 膨胀到 8B,堆净空间直接缩水 10–15%,Full GC 耗时翻倍。
验证脚本:java -XX:+PrintFlagsFinal -Xms32767m -Xmx32767m -version | grep UseCompressedOops输出为 true 即可放心使用 31GB;false 则继续下调 1GB 直到 true。
-
如何设置
systemd 服务:[Service] Environment=ES_JAVA_OPTS="-Xms31g -Xmx31g"容器:
docker run -e ES_JAVA_OPTS="-Xms31g -Xmx31g" -m 62g …禁止只设置 -Xmx 而不设置 -Xms,否则节点重启后 JVM 会按需扩容,造成 3–5 分钟性能抖动。
-
其他内存留给谁
Lucene 段文件缓存:查询阶段热点 term、doc values、norms 都靠 OS cache,经验值每 1GB 堆需要 2–3GB 系统缓存才能打满 SSD IOPS。因此 31GB 堆对应 64GB 物理内存是最经济比。
二、File Descriptors:句柄耗尽 = 集群“雪崩”
-
现象
日志出现java.io.IOException: Too many open files,随后分片重分配、节点掉线、数据丢失。 -
原理
一个分片 = 至少 30–50 个段文件(.tim、.doc、.pos、.dvd…),每个文件占用 1 个 fd;1 个节点 1000 分片就是 5 万句柄,再加上 9200 端口监听、传输连接、恢复文件,轻松突破 65535。 -
推荐值
/etc/security/limits.confelasticsearch soft nofile 655360 elasticsearch hard nofile 655360systemd override:
[Service] LimitNOFILE=655360验证:
curl -s localhost:9200/_nodes/stats/process?filter_path=**.max_file_descriptors返回值需 ≥ 655360。
-
容器场景
Docker 1.12 之后支持 --ulimit nofile=655360:655360,K8s 在 securityContext 中加上limits: - name: nofile soft: 655360 hard: 655360
三、Swappiness:宁可 OOM,也不 swap
-
危害
当系统回收内存时,一旦交换出 Lucene 段文件,查询会触发磁盘随机读,RT 从毫秒级跌到秒级,最终被 master 判定为节点失联。 -
设置
echo 'vm.swappiness=1' >> /etc/sysctl.conf sysctl -p为什么不设 0:RHEL/CentOS 7 内核在内存耗尽时,0 会触发 OOM-killer 直接把 ES 进程杀掉;1 允许紧急交换保命,但几乎不会主动换出。
-
容器注意
宿主机全局 swappiness 对容器生效,若宿主机为 60,容器内再设 1 无效;必须修改宿主机 /proc/sys/vm/swappiness。
四、max_map_count:mmap 失败导致分片无法分配
-
现象
日志报Failed to create engine… mmap failed: Cannot allocate memory,分片状态 UNASSIGNED。 -
原理
Lucene 7+ 默认使用 mmap 读取段文件,每个 1GB 段需要 256 个虚拟内存区域(默认 64KB 粒度),1000 分片 × 50 段 × 256 ≈ 1280 万映射,而默认 vm.max_map_count=65530 远不够。 -
推荐值
echo 'vm.max_map_count=262144' >> /etc/sysctl.conf sysctl -p容器同样依赖宿主机参数,Docker Desktop for Mac/Win 需在 GUI 里调整 “Memory” → “Max map count”。
-
验证
curl -s localhost:9200/_nodes/stats/os?filter_path=**.mmap_count若已用值接近上限,需继续倍扩容。
五、一键巡检脚本
把下列脚本丢到每台节点,5 秒出报告:
#!/bin/bash
printf "%-20s %-10s %-10s %-10s %-10s\n" HOST IP HEAP FD SWAPPINESS MAX_MAP
for ip in $(cat /etc/hosts | awk '/es-/ {print $1}'); do
heap=$(curl -s $ip:9200/_nodes/stats/jvm?filter_path=**.heap_max_in_bytes | jq '.nodes[].jvm.mem.heap_max_in_bytes' | awk '{print int($1/1024/1024/1024)"G"}')
fd=$(curl -s $ip:9200/_nodes/stats/process?filter_path=**.max_file_descriptors | jq '.nodes[].process.max_file_descriptors')
swap=$(ssh $ip "cat /proc/sys/vm/swappiness")
map=$(ssh $ip "cat /proc/sys/vm/max_map_count")
printf "%-20s %-10s %-10s %-10s %-10s\n" $(hostname -s) $ip $heap $fd $swap $map
done
输出示例:
HOST IP HEAP FD SWAPPINESS MAX_MAP
es-hot-001 10.0.0.11 31G 655360 1 262144
es-warm-002 10.0.0.12 31G 655360 1 262144
凡 FD<655360、SWAPPINESS>1、MAX_MAP<262144 的行自动标红,值班人员立即修复。
六、常见误区
-
“内存大就无脑 32GB”
超过 31GB 关闭压缩 Oops 的代价远高于多 1GB 堆的收益,务必用 PrintFlagsFinal 验证。 -
“容器里改 limits.conf 就行”
容器主进程不受 pam_limits 管控,必须走 docker --ulimit 或 K8s securityContext。 -
“swap 关了性能更好”
完全关闭 swappiness=0 在部分内核会触发早期 OOM,留 1 给内核保留逃生通道。 -
“mmap 报错加内存”
本质是虚拟地址区域耗尽,加物理内存无用,调大 max_map_count 才是正解。
七、总结
Heap 31GB、FD 65 万、Swap 1、Map 26 万,这四个数字是 Elastic 官方 Support 工单里出现频率最高的“万能处方”。上线前用巡检脚本一次性批量化修复,能把 80% 的“灵异”问题消灭在投产之前;后续扩容只需横向加节点,再也不用半夜因为“Too many open files”而被电话叫醒。
更多技术文章见公众号: 大城市小农民
434

被折叠的 条评论
为什么被折叠?



