一次 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
后续建议
-
定期监控持久化数据库和 Broker 内存:
bashdu -h /var/lib/mosquitto/mosquitto.db ps -eo pid,user,comm,rss,%mem --sort=-rss | head -10 free -h -
若数据库再次快速增长,优先检查离线客户端、上游桥接状态、Retain 消息数量和
zigbee2mqtt/#双向桥接是否存在不必要的全量同步。 -
Mosquitto 当前版本为
1.4.15,建议在维护窗口升级到受支持版本,以获得更完善的 队列治理和安全修复。 -
MQTT 连接密码不应写入技术文档、截图或日志;如曾暴露,应立即在服务端更换并同步 更新网关配置。
总结
本次问题并非普通的 Linux 缓存占用,而是 Mosquitto 持久化数据库过大,导致 Broker 占用大量物理内存和 Swap。短期通过重启释放内存,最终通过清除历史数据库并限制每个 客户端的队列数量、单条消息大小恢复稳定。持久化可以保留,但需要持续限制积压来源并 监控数据库增长。