Mosquitto 持久化消息积压导致网关内存耗尽的排查

一次 Mosquitto 持久化消息积压导致网关内存耗尽的排查

背景

某 Orange Pi Zigbee 网关运行一段时间后出现内存紧张。etah-mqtt 服务已经停止,但网关依然卡顿,无法通过停止业务服务恢复内存。

本文记录这次问题的定位过程、根因和最终处理方式。

现象

执行 top 时,网关总内存约 1 GB,可用内存仅约 51 MB,Swap 约 500 MB 几乎耗尽:

text 复制代码
KiB Mem : 1023516 total, 36116 free, 942140 used, 45260 buff/cache
KiB Swap: 511756 total, 32 free, 511724 used, 52392 avail Mem

按 RSS 排序进程后,发现问题不在 etah-mqtt,而在 MQTT Broker:

bash 复制代码
ps -eo pid,ppid,user,comm,rss,vsz,%mem --sort=-rss | head -20
text 复制代码
PID  USER       COMMAND      RSS      %MEM
769  mosquitto  mosquitto    702140   68.6

mosquitto 单进程占用约 686 MB 内存,是 Swap 被耗尽的直接原因。

定位过程

先重启 Mosquitto:

bash 复制代码
systemctl restart mosquitto
free -h

重启后可用内存恢复到约 678 MB,Swap 使用量也从约 500 MB 降至约 14 MB。 这确认了内存压力来自 Mosquitto,而不是系统缓存或 etah-mqtt 残留进程。

继续检查持久化文件:

bash 复制代码
ls -lh /var/lib/mosquitto/mosquitto.db

结果如下:

text 复制代码
-rw------- 1 mosquitto mosquitto 921M mosquitto.db

根因

Mosquitto 开启了持久化:

conf 复制代码
persistence true
persistence_location /var/lib/mosquitto/

mosquitto.db 会保存持久客户端会话、离线 QoS 消息和 Retain 保留消息。 数据库增长到约 921 MB 后,Mosquitto 在运行或重启加载数据时占用了大量内存, 最终导致网关物理内存和 Swap 耗尽。

网关还配置了同一主题的双向 QoS 1 桥接:

conf 复制代码
topic zigbee2mqtt/# out 1
topic zigbee2mqtt/# in  1

该配置本身不必然导致问题,但在上游不可用、客户端离线、消息高频或桥接消息回流时, 会增加 QoS 消息积压和持久化数据增长的风险。

最终处理

1. 清除历史持久化数据库

停止 Mosquitto 后直接清除旧数据库,再启动服务:

bash 复制代码
systemctl stop mosquitto
rm -f /var/lib/mosquitto/mosquitto.db
systemctl start mosquitto

该操作会清除以下历史数据:

  • Retain 保留消息;
  • 持久客户端会话;
  • 等待离线客户端接收的 QoS 消息。

生产环境更推荐先移动文件而非直接删除,确认业务正常后再清理备份:

bash 复制代码
systemctl stop mosquitto
mv /var/lib/mosquitto/mosquitto.db \
  /var/lib/mosquitto/mosquitto.db.bak-$(date +%F-%H%M%S)
systemctl start mosquitto

2. 保留持久化并增加保护限制

不建议仅为避免文件增长而关闭持久化。对于设备离线后仍需补发 QoS 消息、保留 设备最后状态或恢复客户端会话的场景,persistence true 仍然有必要。

本次在 Mosquitto 配置中加入以下两项:

conf 复制代码
max_queued_messages 50
message_size_limit 262144
  • max_queued_messages 50:每个客户端最多积压 50 条 QoS 消息。
  • message_size_limit 262144:限制单条 MQTT 消息最大为 256 KB。

修改后重启并确认服务正常:

bash 复制代码
systemctl restart mosquitto
systemctl status mosquitto --no-pager

后续建议

  1. 定期监控持久化数据库和 Broker 内存:

    bash 复制代码
    du -h /var/lib/mosquitto/mosquitto.db
    ps -eo pid,user,comm,rss,%mem --sort=-rss | head -10
    free -h
  2. 若数据库再次快速增长,优先检查离线客户端、上游桥接状态、Retain 消息数量和 zigbee2mqtt/# 双向桥接是否存在不必要的全量同步。

  3. Mosquitto 当前版本为 1.4.15,建议在维护窗口升级到受支持版本,以获得更完善的 队列治理和安全修复。

  4. MQTT 连接密码不应写入技术文档、截图或日志;如曾暴露,应立即在服务端更换并同步 更新网关配置。

总结

本次问题并非普通的 Linux 缓存占用,而是 Mosquitto 持久化数据库过大,导致 Broker 占用大量物理内存和 Swap。短期通过重启释放内存,最终通过清除历史数据库并限制每个 客户端的队列数量、单条消息大小恢复稳定。持久化可以保留,但需要持续限制积压来源并 监控数据库增长。

相关推荐
AINative软件工程2 小时前
LLM Streaming 背压工程实践:慢客户端、断流重连与服务端缓冲区的生产设计
node.js·llm
码林鼠15 小时前
webpack的基本配置
前端·webpack·node.js
FeelTouch Labs18 小时前
Node.js 中的 spawn 和 exec:子进程执行的不同方式对比
node.js·编辑器·vue·vim
烂蜻蜓20 小时前
Node.js入门教程(一):初识Node.js——服务端JavaScript的诞生与特点
开发语言·javascript·node.js
凌云拓界1 天前
NodeVerdict:Node.js 原生诊断数据可视化工具
信息可视化·架构·typescript·开源·node.js·github·bug
用户2930750976691 天前
React Router v6 实战:理解 SPA 路由系统
node.js
星蓝_starblue2 天前
零服务器、零数据库!开源growth-board,利用GitHub自动管理刷题/学习/求职全流程
服务器·数据库·程序人生·系统架构·node.js·github·改行学it
梦想CAD控件2 天前
网页端CAD的图形选择、编辑与夹点操作教程
前端·javascript·node.js
万敏2 天前
Vue3 全栈实战第四周:watch、nextTick、性能优化与 keep-alive 实战记录
vue.js·node.js·全栈