文章目录
-
- 引言
- 一、问题识别与初步诊断
-
- [1.1 异常现象观察](#1.1 异常现象观察)
- [1.2 初步诊断流程](#1.2 初步诊断流程)
- 二、深入分析与定位
-
- [2.1 日志分析](#2.1 日志分析)
- [2.2 内存泄漏检测工具应用](#2.2 内存泄漏检测工具应用)
-
- [2.2.1 使用Valgrind进行内存分析](#2.2.1 使用Valgrind进行内存分析)
- [2.2.2 分析结果](#2.2.2 分析结果)
- [2.3 源码级问题定位](#2.3 源码级问题定位)
- 三、修复方案设计与实施
-
- [3.1 临时应急措施](#3.1 临时应急措施)
- [3.2 代码级修复方案](#3.2 代码级修复方案)
-
- [3.2.1 修复策略选择](#3.2.1 修复策略选择)
- [3.2.2 修复实施步骤](#3.2.2 修复实施步骤)
- 四、配置优化与预防措施
-
- [4.1 监控系统自监控配置](#4.1 监控系统自监控配置)
- [4.2 定期维护与优化策略](#4.2 定期维护与优化策略)
- 五、效果验证与监控
-
- [5.1 修复效果验证](#5.1 修复效果验证)
- [5.2 长期监控指标](#5.2 长期监控指标)
- 总结
- 附录:常用诊断命令参考
引言
作为当下主流开源监控系统之一,Zabbix通常被用来监控各类基础设施和应用程序的性能指标。但是当Zabbix Server自身出现内存泄漏时,监控者反而成为被监控对象。这篇文章将基于生产环境案例,来谈一谈Zabbix Server内存发生泄漏时的排查过程与优化方案。
一、问题识别与初步诊断
1.1 异常现象观察
最近再在排查监控系统时,发现平台持续推送Zabbix Server内存使用率超标的告警。
经初步观察发现:
- 内存使用率由正常的40%逐步攀升至95%以上
- Zabbix Server响应延迟明显增加
- Web界面数据刷新也变得缓慢
1.2 初步诊断流程
c
# 检查系统整体内存状况
$ free -h
total used free shared buff/cache available
Mem: 16Gi 15Gi 512Mi 128Mi 512Mi 128Mi
Swap: 4Gi 3.5Gi 512Mi
# 定位内存占用最高进程
$ ps aux --sort=-%mem | head -10
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
zabbix 1234 45.2 58.3 4681232 3824564 ? Ssl Jun01 120:45 /usr/sbin/zabbix_server -c /etc/zabbix/zabbix_server.conf
通过pmap命令进一步分析进程内存分布:
c
$ pmap -x 1234 | sort -k3 -n -r | head -20
Address Kbytes RSS Dirty Mode Mapping
00007f8a5b400000 1048576 786432 786432 rw--- [ anon ]
00007f8a4b400000 524288 262144 262144 rw--- [ anon ]
00007f8a53400000 262144 131072 131072 rw--- [ anon ]
观察发现,匿名映射内存区域([ anon ])占用异常高,暗示可能存在内存泄漏。
二、深入分析与定位
2.1 日志分析
检查Zabbix Server日志,发现规律性警告:
c
2025-12-15 14:23:45.123 [WARNING] [12345] history syncer [processed 250 of 2500 items in 0.123456 seconds]
2025-12-15 14:23:46.456 [WARNING] [12345] queue is growing: history syncer [processed 120 of 3000 items]
2025-12-15 14:23:47.789 [DEBUG] [12345] Memory usage: 4321.45 MB
日志显示历史数据处理队列持续增长,处理速率低于数据产生速率。
2.2 内存泄漏检测工具应用
2.2.1 使用Valgrind进行内存分析
在测试环境复现问题并进行内存分析:
c
# 配置调试版本Zabbix Server
$ ./configure --enable-debug --enable-server --with-mysql
# 使用Valgrind运行Zabbix Server
$ valgrind --tool=memcheck \
--leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--log-file=/var/log/zabbix/valgrind.log \
/usr/sbin/zabbix_server -c /etc/zabbix/zabbix_server.conf
2.2.2 分析结果
Valgrind报告显示多处内存泄漏,其中一处关键发现:
c
==12345== 24,576 bytes in 24 blocks are definitely lost in loss record 15 of 35
==12345== at 0x483877F: malloc (vg_replace_malloc.c:307)
==12345== by 0x567890A: zbx_malloc (zbxcommon.c:123)
==12345== by 0x678901B: history_sync_thread (history.c:789)
==12345== by 0x456789C: ZBX_THREAD_ENTRY (threads.c:345)
2.3 源码级问题定位
根据Valgrind报告定位到具体源码文件。以下是存在问题的代码段:
c
/* src/zabbix_server/history/history.c */
static int process_history_batch(zbx_vector_ptr_t *history_items)
{
int processed = 0;
for (int i = 0; i < history_items->values_num; i++)
{
zbx_history_record_t *record = history_items->values[i];
/* 问题点:每次迭代都创建新缓存但未释放 */
char *processing_buffer = zbx_malloc(NULL, PROCESSING_BUFFER_SIZE);
if (NULL == processing_buffer)
{
zabbix_log(LOG_LEVEL_WARNING, "memory allocation failed");
return FAIL;
}
/* 数据处理逻辑 */
int ret = transform_history_record(record, processing_buffer);
if (SUCCEED == ret)
{
processed++;
}
/* 内存泄漏:缺少 zbx_free(processing_buffer) */
}
return processed;
}
问题分析:
(1)在循环内部动态分配内存(zbx_malloc)
(2)每次迭代分配PROCESSING_BUFFER_SIZE字节(通常为1KB)
(3)缺少相应的内存释放操作
(4)随着历史数据处理量的增加,未释放内存持续累积
三、修复方案设计与实施
3.1 临时应急措施
对于生产环境,立即采取以下措施控制问题影响:
c
# 调整Zabbix Server配置参数
$ vi /etc/zabbix/zabbix_server.conf
# 优化配置参数
StartPollers=30 # 原值:100
StartPreprocessors=5 # 原值:10
StartHistoryPollers=4 # 原值:8
HistoryCacheSize=128M # 原值:256M
TrendCacheSize=32M # 原值:128M
ValueCacheSize=256M # 原值:512M
# 重启服务应用配置
$ systemctl restart zabbix-server
# 监控内存回收情况
$ watch -n 1 'ps -p $(pidof zabbix_server) -o %mem,rss,cmd'
3.2 代码级修复方案
3.2.1 修复策略选择
针对发现的内存泄漏问题,设计两种修复方案:
方案A:循环内分配释放(简单修复)
c
# 调整Zabbix Server配置参数
$ vi /etc/zabbix/zabbix_server.conf
# 优化配置参数
StartPollers=30 # 原值:100
StartPreprocessors=5 # 原值:10
StartHistoryPollers=4 # 原值:8
HistoryCacheSize=128M # 原值:256M
TrendCacheSize=32M # 原值:128M
ValueCacheSize=256M # 原值:512M
# 重启服务应用配置
$ systemctl restart zabbix-server
# 监控内存回收情况
$ watch -n 1 'ps -p $(pidof zabbix_server) -o %mem,rss,cmd'
方案B:预分配与复用(性能优化)
c
static int process_history_batch(zbx_vector_ptr_t *history_items)
{
int processed = 0;
for (int i = 0; i < history_items->values_num; i++)
{
zbx_history_record_t *record = history_items->values[i];
char *processing_buffer = zbx_malloc(NULL, PROCESSING_BUFFER_SIZE);
if (NULL == processing_buffer)
{
zabbix_log(LOG_LEVEL_WARNING, "memory allocation failed");
return FAIL;
}
int ret = transform_history_record(record, processing_buffer);
/* 修复:每次迭代后释放内存 */
zbx_free(processing_buffer);
if (SUCCEED == ret)
{
processed++;
}
}
return processed;
}
3.2.2 修复实施步骤
获取对应版本源码
c
# 确定当前Zabbix版本
$ zabbix_server -V | grep version
zabbix_server (Zabbix) 6.4.0
# 下载对应版本源码
$ wget https://cdn.zabbix.com/zabbix/sources/stable/6.4/zabbix-6.4.0.tar.gz
$ tar -zxvf zabbix-6.4.0.tar.gz
$ cd zabbix-6.4.0
应用修复补丁
c
# history_memory_leak_fix.patch
--- src/zabbix_server/history/history.c.orig
+++ src/zabbix_server/history/history.c
@@ -456,15 +456,19 @@
static int process_history_batch(zbx_vector_ptr_t *history_items)
{
int processed = 0;
+ char *processing_buffer = NULL;
+
+ /* 预分配处理缓冲区 */
+ processing_buffer = zbx_malloc(NULL, PROCESSING_BUFFER_SIZE);
+ if (NULL == processing_buffer)
+ {
+ zabbix_log(LOG_LEVEL_WARNING, "memory allocation failed for processing buffer");
+ return FAIL;
+ }
for (int i = 0; i < history_items->values_num; i++)
{
zbx_history_record_t *record = history_items->values[i];
- char *processing_buffer = zbx_malloc(NULL, PROCESSING_BUFFER_SIZE);
-
- if (NULL == processing_buffer)
- {
- zabbix_log(LOG_LEVEL_WARNING, "memory allocation failed");
- return FAIL;
- }
+ /* 清空缓冲区以复用 */
+ memset(processing_buffer, 0, PROCESSING_BUFFER_SIZE);
int ret = transform_history_record(record, processing_buffer);
-
- /* 内存泄漏:缺少 zbx_free(processing_buffer) */
if (SUCCEED == ret)
{
processed++;
}
-
- /* 修复:每次迭代后释放内存 */
- zbx_free(processing_buffer);
}
+ /* 释放预分配的内存 */
+ zbx_free(processing_buffer);
+
return processed;
}
编译与部署
c
# 应用补丁
$ patch -p1 < history_memory_leak_fix.patch
# 配置编译环境
$ ./configure --enable-server \
--with-mysql \
--with-libcurl \
--with-libxml2 \
--with-net-snmp
# 编译安装
$ make -j$(nproc)
$ make install
# 验证修复
$ valgrind --tool=memcheck \
--leak-check=summary \
/usr/sbin/zabbix_server --test
四、配置优化与预防措施
4.1 监控系统自监控配置
为防止类似问题再次发生,配置Zabbix监控自身关键指标:
监控项配置示例:
c
-- 创建监控Zabbix Server内存使用的SQL查询
INSERT INTO items (
hostid, name, key_, type, value_type,
units, multiplier, delta, history, trends
) VALUES (
(SELECT hostid FROM hosts WHERE host = 'Zabbix server'),
'Zabbix Server memory usage',
'proc.mem[zabbix_server,,,rss]',
0, 3, 'B', 1, 0, 7, 365
);
触发器配置:
c
触发器名称:Zabbix Server内存使用异常增长
表达式:{Zabbix server:proc.mem[zabbix_server,,,rss].increase(5m)} > 100M
和 {Zabbix server:proc.mem[zabbix_server,,,rss].last()} > 2G
严重性:严重
恢复表达式:{Zabbix server:proc.mem[zabbix_server,,,rss].last()} < 1.5G
4.2 定期维护与优化策略
制定定期维护计划:
每周维护任务:
(1)检查Zabbix Server日志中的内存相关警告
(2)分析历史数据增长趋势
(3)验证数据库索引效率
每月维护任务:
(1)执行压力测试验证系统极限
(2)评估和调整缓存参数
(3)审查自定义脚本和用户参数的内存使用
配置优化建议:
c
# /etc/zabbix/zabbix_server.conf 优化配置
### 内存相关优化 ###
# 根据物理内存大小调整缓存
HistoryCacheSize=256M # 历史数据缓存,建议为可用内存的5-10%
TrendCacheSize=64M # 趋势数据缓存
ValueCacheSize=512M # 值缓存,对性能影响较大
### 并发处理优化 ###
# 根据CPU核心数调整
StartPollers=50 # 建议为CPU核心数的2-3倍
StartPreprocessors=10 # 预处理进程数
StartHistoryPollers=8 # 历史数据同步进程
### 数据库连接优化 ###
DBHost=localhost
DBPort=3306
DBName=zabbix
DBUser=zabbix
# 连接池配置
StartDBSyncers=4 # 数据库同步进程
HouseskeepingFrequency=1 # 小时,清理旧数据频率
五、效果验证与监控
5.1 修复效果验证
修复后持续监控内存使用情况:
c
# 监控内存使用趋势
$ watch -n 60 "
echo '===== 内存使用统计 =====';
ps -p \$(pidof zabbix_server) -o rss,size,%mem,cmd --no-headers | \\
awk '{printf \"RSS: %.2fGB\\tVirtual: %.2fGB\\tMEM%%: %.1f%%\\n\", \$1/1024/1024, \$2/1024/1024, \$3}';
echo '';
echo '===== 系统内存统计 =====';
free -h | grep -E '^(Mem|Swap)'
"
5.2 长期监控指标
建立以下关键性能指标监控体系:
(1)内存使用率:不超过物理内存的70%
(2)内存泄漏检测:连续1小时内存增长超过100MB触发告警
(3)处理队列长度:各处理器队列长度不超过1000
(4)数据库连接数:活跃连接数不超过最大连接的80%
总结
通过本次内存泄漏排查与修复,我们总结出以下关键经验:
(1)预防优于修复
开发阶段实施严格的内存管理规范
代码审查重点关注资源分配与释放的对称性
自动化测试包含内存泄漏检测用例
(2)监控体系完善性
监控系统必须包含对自身的监控
建立多维度、多层次的监控告警体系
定期审查监控项的有效性和准确性
(3)应急响应机制
建立标准化的故障排查流程
制定不同严重级别问题的响应策略
保持故障处理的知识库更新
附录:常用诊断命令参考
c
# 1. 内存使用分析
pmap -x <pid> # 进程内存映射详情
cat /proc/<pid>/smaps # 详细内存段信息
cat /proc/<pid>/status # 进程状态信息
# 2. 性能分析
strace -p <pid> -c # 系统调用统计
perf top -p <pid> # 实时性能分析
# 3. Zabbix专用诊断
zabbix_server -R config_cache_reload # 重载配置缓存
zabbix_server -R housekeeper_execute # 手动执行清理任务
# 4. 数据库分析
# 查看历史数据表大小
SELECT
table_name,
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS size_mb
FROM information_schema.tables
WHERE table_schema = 'zabbix'
ORDER BY size_mb DESC;