FE / 协调节点内存满问题故障排查
防止"FE内存已满"问题,快速恢复,并尽可能找出原因以防止再次发生。
- This information is applicable to FE nodes and Coordinator nodes (shared-data deployments). For readability, we will use "FE" to refer to both types of frontend nodes.
- This article mainly analyzes the situation and solutions of in-heap memory, without covering off-heap memory. If the OOM is caused by off-heap memory issues, consider reducing the JVM XMX configuration or expanding the machine's memory.
FE 内存结构与分配建议
FE 内存组成
前端服务(FE)运行在 JVM 上,其内存消耗来自 Java 堆内存和堆外内存开销。
JVM 内存模块概述
在FE进程中,通过JVM分配的所有内存大致可以分为以下几个模块:
| 模块 | 类型 | 描述 |
|---|---|---|
| Java 堆 | 堆 | Java 对象实例的内存,例如 Plan Cache、元数据、临时对象等。 |
| 类 | 堆外 | 加载 Class 时生成的元数据等结构 |
| 线程 | 堆外 | 线程占用的内存,主要是每个线程的栈空间(通常每个线程约 1MB)。 |
| 代码 | 堆外 | JVM 将字节码编译为本机机器码时占用的内存 |
| GC | 堆外 | 垃圾收集器的内部工作数据结构 |
| 编译器 | 堆外 | HotSpot 编译器使用的内存可能与 Code 的内存类似。 |
| 其他 | 堆外 | 包括直接内存,如 Direct ByteBuffer、Netty buffer 等。 |
| 符号 | 堆外 | SymbolTable 结构,如字符串常量池、整数常量池等。 |
| 本机内存跟踪 (NMT) | 堆外 | NMT 自身记录的内存使用所占用的内存 |
| Arena 块 | 堆外 | JVM 内部使用 malloc 分配的临时内存块,用于临时存储和重用 |
| 日志记录 | 堆外 | 日志系统(如 log4j)占用的内存 |
| 参数 | 堆外 | JVM 启动参数传递所占用的内存 |
| 模块 | 堆外 | Java 模块系统中使用的结构 |
考虑到JVM和StarRocks的运行特性,FE的内存主要由以下几部分组成:
-
JVM堆内存(Heap)
- 存储 Java 对象实例,例如 Plan Cache、元数据缓存等。
- 最大大小通过参数
-Xmx设置,并受垃圾回收机制的显著影响。
-
非堆内存(Metaspace)
- 存储类定义、常量池等元数据。
- Java 8 及更高版本替代了 PermGen。
-
直接内存
- Netty、ByteBuffer、日志缓冲区、RPC 框架等使用的堆外内存。
- 由
sun.misc.Unsafe分配,通常不受 GC 控制。
-
线程栈内存
- 每个 Java 线程在创建时会申请一个线程栈,默认大小约为 1MB(可通过
-Xss配置)。 - 当线程数量较多时(例 如高并发场景),容易导致内存积压。
- 每个 Java 线程在创建时会申请一个线程栈,默认大小约为 1MB(可通过
-
JNI / 本地内存调用
- 数据库在通过日志组件(如 RocksDB、Netty)、Cache 等调用本地层时可能会消耗内存。
JVM 参数配置建议
在 JVM 中,堆内存通过 Xmx 进行配置。通常,进程使用的内存小于 Xmx 的 130% 是合理的。也就是说,除了 JVM Xmx 配置的内存之外,还会使用一部分堆外内存。例如,如果 JVM 配置了 21g,则在 top 中看到的进程 RSS 内存使用量约为 27g。
根据经验,tablet 数量与 FE 内存的推荐关系如下:
| Tablet 数量 | 推荐 FE 内存配置 |
|---|---|
| 少于 1M | 16 GB |
| 1M - 2M | 32 GB |
| 2M - 5M | 64 GB |
| 5M - 10M | 128 GB |
对于独立部署环境,建议为系统预留足够的内存。除了为系统预留的内存外,还需要考虑堆外内存。
对于混合部署环境,需要根据其他服务的使用情况尽可能预留足够的内存,以防止 OOM。
在 FE 独立部署的情况下(不建议混合部署):
- 当机器内存小于 32G 时,
-Xmx(最大堆大小)应配置为机器内存的最多 70%。 - 当机器内存大于 32G 时,
-Xmx(最大堆大小)应配置为机器内存的最多 80%。 - 由于 FE leader 节点需要执行 checkpoint,通常情况下,leader 节点的内存约为 follower 节点的两倍。
在 3.4+ 版本中,leader 会选择一个 follower 节点执行 checkpoint,然后下载 follower 的结果并分发给其他 follower。leader 的内存使用量将接近 follower 的内存使用量。请参阅PR #52103。
FE JVM 监控与内存 Profile 部署
对于 FE 状态,堆监控和 GC 监控能够有效监控服务状态,也是事后排查问题的关键线索。建议及时配置监控,并在必要时配置相应的告警。
具体监控指标如下:
| 指标 | 描述 |
|---|---|
jvm_heap_size_bytes | 堆内存使用量。 |
jvm_non_heap_size_bytes | 主要包括 metaspace、代码缓存等,不包含所有堆外内存。 |
jvm_old_gc{type="count"} | Full GC 次数。 |
jvm_old_gc{type="time"} | Full GC 时间。 |
这些指标及其他指标的监控信息请参阅监控与告警
内存 profile 可以分析堆内存的突然增长 。需要在服务安装完成后确认其是否正常工作。
在 3.3.6+ 版本中,数据库会定期打印内存 profile 并输出到 log/proc_profile,格式为 HTML,通过 tgz 压缩。
控制配置:
proc_profile_collect_interval_s = 600
proc_profile_collect_time_s = 300
proc_profile_cpu_enable = true
proc_profile_mem_enable = true
对于 3.3.6 之前的版本,可以通过脚本定期打印 profile。在每个 FE 的安装目录下新建一个名为 mem_profiler.sh 的脚本文件,并将以下内容复制到该文件中。
此脚本对 FE 进程无影响,不影响服务的正常使用,并会自动清理已过期 48 小时的文件。
#!/bin/bash
mkdir -p mem_alloc_log
cleanup_old_files() {
find mem_alloc_log -name "alloc-profile-*.html" -mmin +2880 -exec rm -f {} \;
}
while true
do
cleanup_old_files
current_time=$(date +'%Y-%m-%d-%H-%M-%S')
file_name="mem_alloc_log/alloc-profile-${current_time}.html"
./bin/profiler.sh -e alloc --alloc 2m -d 300 -f "$file_name" `cat bin/fe.pid`
done
# 后台启动
chmod +x mem_profiler.sh
nohup ./mem_profiler.sh > mem_profiler.out 2>&1 &
# 检查进程是否存在
ps aux | grep mem_profiler.sh
# 停止进程
pkill -f mem_profiler.sh
FE 崩溃
FE 崩溃的原因大致分为 4 种类型。
1. FE 进程内存占用过高,被操作系统杀死
您可以通过查询系统日志来检查是否为此原因:
# 查看 dmesg
dmesg | grep -iE 'out of memory|oom|kill|killed process'
# 查看消息日志
sudo grep -Ei 'killed process|oom.kill|sending SIG' -C5 /var/log/messages /var/log/syslog 2>/dev/null | tail -n 100
被 OOM 杀死后,日志可能会打印系统进程的内存使用情况。OOM 显示的 total_vm 和 rss 等字段以页为单位,默认 1 页 = 4KB:
total_vm = 8793525→ 表示已申请约 8793525 × 4KB ≈ 33.5 GB 的虚拟内存rss = 7218120→ 表示实际已使用约 27.5 GB 的物理内存,即 7218120 × 4KB
OOM 日志示例:
Jun 10 11:20:24 tp-prod-bigdata-sr-ss-fe-2-b kernel: [ pid ] uid tgid total_vm rss nr_ptes swapents oom_score_adj name
Jun 10 11:21:58 tp-prod-bigdata-sr-ss-fe-2-b kernel: [22654] 1005 22654 8793525 7218120 14943 0 0 java
检查实际 RSS 使用量。如果非常接近机器内存,通常是由于 JVM 分配不合理。请参考JVM 参数配置建议章节。