Redis 宕机排查与修复报告
Redis 宕机排查与修复报告
jkunonlineRedis 宕机排查与修复报告
- 日期:2026-07-07
- 服务器:185.158.137.66(hostname: love,Debian,16G 内存 + 4G swap)
- 项目:V2Board 面板(
/www/wwwroot/api123.52pokemon.cc,宝塔 + php-fpm 8.1 + MySQL + Redis + Horizon 队列)
一、故障现象
- 今天 12:01 Redis 宕机(此前 7月1日 14:37 也发生过一次同样的宕机)。
- 宕机后由 redis-watchdog 定时脚本自动拉起,数据(587MB dump.rdb)无丢失。
二、排查结论
不是攻击,不是泄漏
- 宕机前请求量平稳(每分钟 1200~1400 次,与日常基线一致),无 CC/DDoS 流量尖峰;12:01 的小峰值是宕机恢复后的积压请求回放。
- 来源 IP 均为 Cloudflare 回源节点、自有代理节点服务器(轮询 UniProxy 接口)、自有客户端 App(UA: SuperAccelerator),无爆破、无异常扫描命中。
api_attack.log为空;内核里的[UFW BLOCK]仅为互联网常规端口扫描噪音,均被防火墙拦截。
真实原因:群发提醒邮件 → 内存耗尽 → OOM 连环击杀
完整链条:
- 11:30 V2Board 定时任务
send:remindMail(Kernel.php中dailyAt('11:30'))触发,一次性向队列塞入 2545 封邮件(2043 封”服务即将到期” + 498 封”流量达 95%”,数据来自v2_mail_log表)。 - 该任务代码用
User::all()一次性加载 38.7 万用户进内存,且ini_set('memory_limit', -1)不设上限。 - Horizon 队列
balance=auto、maxProcesses按内存公式算出 94,队列堆积后疯狂扩容 worker。 - OOM 瞬间(12:00:23 内核快照):php-fpm 151 个进程共占约 12.5GB + Horizon php 进程 65 个约 1.2GB + mysqld 1.7GB,16G 物理内存 + 4G swap 全部打满。
- 内核 OOM Killer 依次击杀:12:00:23 mysqld → 12:01:08 systemd/sd-pam → **12:01:08 redis-server(两个实例)**。
- Redis 是”连坐”受害者(自身 RSS 仅约 70MB),真正的元凶是邮件群发引发的 php 进程风暴。
三、已执行的修改
修改的文件(共 3 个,均在服务器 185.158.137.66 上)
| # | 文件 | 改动内容 |
|---|---|---|
| 1 | /www/wwwroot/api123.52pokemon.cc/app/Console/Commands/SendRemindMail.php |
① 删除 ini_set('memory_limit', -1);② User::all() 全表加载 → WHERE (remind_expire=1 OR remind_traffic=1 OR telegram_id IS NOT NULL) 预筛 + chunkById(500) 分块读取;③ 新增错峰逻辑:每 100 封邮件入队延迟 +60 秒(常量 BATCH_SIZE=100、BATCH_DELAY_SECONDS=60 可调) |
| 2 | /www/wwwroot/api123.52pokemon.cc/app/Services/MailService.php |
remindExpire() 和 remindTraffic() 增加 int $delaySeconds = 0 参数,SendEmailJob::dispatch(...) 改为 ->delay(now()->addSeconds($delaySeconds)) 延迟入队;两方法返回入队邮件数(int),供命令统计 |
| 3 | /www/wwwroot/api123.52pokemon.cc/config/horizon.php |
supervisor 一拆二: • V2board(order_handle / traffic_fetch / stat / send_telegram):maxProcesses 由动态公式(算出 94)改为固定 15• V2boardMail(send_email / send_email_mass,新增):maxProcesses = 2 |
备份位置(与原文件同目录,root 所有)
1 | /www/wwwroot/api123.52pokemon.cc/app/Console/Commands/SendRemindMail.php.bak-claude-20260707-124443 |
回滚方法(如需):
1 | cd /www/wwwroot/api123.52pokemon.cc |
部署与验证(已完成)
- ✅ 三个文件
php -l语法检查通过,属主已改回www:www - ✅
php artisan config:cache重建配置缓存 - ✅
php artisan horizon:terminate重启 Horizon(由宝塔 supervisord 自动拉起) - ✅ 新 supervisor 已生效:
V2board max-processes=15、V2boardMail max-processes=2 - ✅ 所有队列积压为 0,各队列均有 worker 正常消费
- ✅ 内存正常:used 7.9G / available 7.7G
四、对业务的影响评估
- 用户代理流量、节点延迟:零影响。代理流量转发不经过面板;节点与面板的通信(UniProxy 拉取用户/上报流量)走 php-fpm HTTP 接口,不走 Horizon 队列,本次未改动。
- 订单、流量记账、TG 通知:零影响。这些队列日常仅需个位数 worker,15 上限充足(验证时:积压 0)。
- 邮件:日常零星邮件(验证码、工单回复)仍秒级送达。仅每天 11:30 群发时,2500 封从”1 分钟内轰完”变为”约 25 分钟内匀速发完”,用户无感知。
- 边界情况:群发期间的验证码邮件可能延迟 1~2 分钟(与群发共用 send_email 队列)。如在意,可将验证码拆到独立高优先队列。
五、遗留事项(待处理)
- 观察明天 11:30 群发:第一次实战检验,建议中午看一眼
free -h、v2_mail_log发信是否正常、Horizon 是否稳定。 - 群发时间仍在业务高峰:
Kernel.php的dailyAt('11:30')建议改到凌晨低峰(如04:30),一行改动。 - 清理大日志,可释放约 22GB 磁盘:
storage/logs/laravel-2026-05-16.log单文件 21GB,以及 5 月其他大日志。 - php-fpm 仍未限制(OOM 时曾 151 进程 / 12.5GB):建议
pm.max_children降到 ~50,加pm.max_requests = 500。 - redis/mysql 无 OOM 保护:建议 systemd unit 加
OOMScoreAdjust=-800,防止再次被优先击杀。 - MySQL 内存:检查
innodb_buffer_pool_size,16G 多服务机器建议 ≤ 2G。
报告生成:2026-07-07,由 Claude Code 排查并执行修复