cron_schedule 中大量状态变成 missed,主机时间、PHP 时区看起来都正常。Missed 的含义不是“系统时间错误”,而是调度器实际处理这条记录时,已经晚于其 scheduled_at 加允许寿命。
先看迟到分布
SELECT job_code, status, COUNT(*) AS jobs,
MIN(scheduled_at) AS oldest, MAX(scheduled_at) AS newest
FROM cron_schedule
WHERE created_at > UTC_TIMESTAMP() - INTERVAL 6 HOUR
GROUP BY job_code, status
ORDER BY jobs DESC;
只有一个 job missed,查该 Group 或任务;所有任务按同一时间段 missed,查系统 Cron 没运行、部署暂停或 PHP 资源耗尽。
系统入口频率要匹配
Magento 通常需要每分钟触发 cron:run。系统 crontab 写成每五分钟,而 schedule_lifetime 很短,任务自然在被扫描前过期。检查实际运行用户、环境 PATH、日志重定向和旧 release 的残留入口。
长任务能拖住同 Group
一个导出或同步占用数十分钟,后续任务排队。记录 executed_at 到 finished_at 的 P95,并考虑把独立业务放到单独 Cron Group、队列或分批任务。仅增加 lifetime 会让 missed 变少,却不会减少业务延迟。
调度生成参数
schedule_generate_every 与 schedule_ahead_for 控制何时生成未来记录,history 清理参数控制表规模。生成窗口太短且调度器中断,恢复后可能根本没有应执行记录;窗口过长又会堆积大量 pending。配置要与任务频率和故障恢复目标匹配。
锁与多节点
多个节点运行同一 Cron Group 必须共享正确锁;锁残留或本地锁不一致会出现一边不执行、一边争抢。结合进程列表、锁后端和 cron_schedule 判断,不能直接把 missed 全改 pending。修复后故意暂停系统入口数分钟再恢复,确认任务能按预期补跑或明确跳过,且不会重复执行业务动作。
表太大也会拖慢调度
cron_schedule 长期不清理,查询 pending 与插入未来记录会变慢。统计表行数、索引和 history cleanup 是否成功;清理应按状态和时间分批执行,不能在生产高峰 truncate,因为正在运行任务与诊断证据都会丢失。
资源饥饿的证据
把 missed 时间与 PHP-FPM/CLI 进程数、CPU、内存、I/O、MySQL 连接和队列积压对齐。CLI memory_limit 与 Web 不同,任务被 OOM kill 时可能没有正常 finished_at。系统日志中的 exit code 比 cron_schedule 一列更能说明原因。
对关键任务定义“最晚完成时间”和独立告警,例如库存同步迟到十分钟与 Sitemap 迟到一天的业务影响不同,不能只用统一 missed 数量阈值。
任务的幂等决定能否补跑
Missed 任务是否可以手工重跑,要看业务动作是否幂等。清缓存和生成报表通常可重复;扣库存、发券、推送订单若没有业务唯一键,补跑会产生第二次副作用。为关键 job 记录处理游标和唯一事件 ID,再制定补偿方案。

