不少站点的问题不是出在白天,而是出在凌晨三点:定时任务跑了一半,队列堆了几十万条,数据库连接被占满,第二天早上访客打开页面看到的就是 502。这类故障排查起来很花时间,因为触发它的脚本平时看起来「一直都没事」。花半小时把定时任务和后台队列过一遍,成本很低。
先列出所有会自动跑的东西
自查第一步不是优化,而是搞清楚到底有谁在跑。常见的入口有这几处:
- 系统的 crontab,包括 root 和各站点用户各自的;
- systemd timer,容易被忽略,因为它不在 crontab -l 的输出里;
- 框架自带的任务调度,比如定时命令、计划任务配置;
- 常驻的队列消费者进程,通常由 supervisor、pm2 一类工具托管;
- 数据库自身的定时事件、备份脚本、日志轮转。
把这些列成一张表,写清楚:做什么、多久跑一次、大概跑多久、跑的时候占用多少 CPU 和磁盘 IO。很多人第一次做完这张表就会发现,有两三个任务其实是重复的。
最容易踩的四个坑
任务重叠执行
一个任务每 5 分钟跑一次,但偶尔要跑 8 分钟,于是第二轮在上一次还没结束时又启动了,两边同时改同一批数据。解决办法是加锁:用文件锁、Redis 锁或数据库标记,拿不到锁就直接退出,并且记一条日志说明这次被跳过了。
挑错了时间窗口
全量重建索引、生成站点地图、批量压缩图片这类重活,如果排在访问高峰,CPU 和磁盘 IO 被抢走,页面响应就会变慢。把它们挪到流量低谷,并且给任务设一个明确的结束时间,超时就中断,不要放任它一直跑。
失败没人知道
定时任务失败最典型的表现是「安静」。脚本报错退出, cron 把错误邮件发到一个没人看的邮箱,然后就没有然后了。至少要做到三件事:非零退出码写进日志、连续失败触发告警、每天早上一封汇总。
队列只涨不消
队列堆积通常有两个原因:生产者写入速度超过消费者处理速度,或者消费者因为一条坏消息卡住反复重试。要盯的是队列长度随时间的变化曲线,而不是某一时刻的数字。
队列相关的几个参数
- 并发数:调大不一定更快,先确认下游数据库能不能承受;
- 重试次数与退避:无限制重试会把一条坏消息放大成几百次请求;
- 可见性超时:设得太短,同一条消息可能被多个消费者重复处理;
- 死信队列:处理不了的消息要有地方去,而不是被直接丢掉;
- 幂等:任务重跑一遍,不应该产生重复数据。
一份可执行的检查清单
- 列出全部定时任务,标注频率、耗时和资源占用;
- 给每一个会写数据的任务加锁并做幂等处理;
- 把重任务移出访问高峰时段;
- 确认失败有日志、有告警、有人负责看;
- 监控队列长度和消费者进程的存活状态;
- 定期清理僵尸进程和过期的锁文件;
- 每季度复查一次,任务会随着业务增长悄悄变重。
定时任务和队列不需要做得多复杂,但需要被看见。能在出问题的十分钟内发现它,比事后写一份复盘报告划算得多。