一次基于redis做队列消费cannel的binlog导致内存被打爆的场景分析
问题复现流程图:
- 100万次 SQL 更新 → 100万个 Binlog 事件
- 100万个事件 → 100万个队列任务(都在同一个业务队列中)
- 100万个任务 → 100万个监控 Key(Horizon 监控每个任务)
每次 MySQL UPDATE 都会产生一个 Binlog 事件 100万次 UPDATE = 100万个 Binlog 事件
- Canal 为每个 Binlog 事件创建一个队列任务
php
foreach ($binlogEvents as $event) {
// 每个事件都创建一个任务,塞到同一个队列
Redis::lpush('ai_vending_horizon', json_encode([
'id' => generateUUID(),
'event' => $event,
'timestamp' => time()
]));
}
horizon:laravel框架的queue队列管理服务 支持队列中每个任务的监控和管理 `
- 暂停/恢复队列
- 重试失败任务
- 清空队列
- 查看任务详情`
- 任务超时
等等
Laravel Horizon 本身是个很好的工具,但在事故中:
- 监控开销过大:每个任务都创建多个监控 Key
- 缺乏批量处理:没有针对大批量任务的优化
- 内存管理缺失:没有监控 Key 数量的限制
- 缺乏redis内存监控告警
Horizon都干了啥?
arduino
// 每个任务都会创建这些监控 Key
ai_phone_horizon:{task_id} // 任务状态
ai_phone_horizon:{task_id}:metrics // 性能指标
ai_phone_horizon:{task_id}:runtime // 运行时间
ai_phone_horizon:{task_id}:memory // 内存使用
ai_phone_horizon:{task_id}:failed // 失败信息
ai_phone_horizon:{task_id}:retries // 重试次数
任务级别的监控的作用
diff
// 需要知道每个任务的状态
- 任务是否成功执行?
- 任务执行了多长时间?
- 任务消耗了多少内存?
- 任务失败了吗?
- 任务重试了几次?
监控开销计算
ini
100万个任务 × 每个任务6个监控Key = 600万个 Redis Key
同时业务逻辑消费慢,任务被阻塞,导致任务队列数据数据越来越多,越来越大,变成一个吃内存的超级大key
优化、优化