5 点 07 分,值班手机开始响。生鲜小程序的开单接口陆续报超时,到 5 点 20 攒了 40 多笔失败,仓库那边电话打进来,说有几张单提交不了,司机等着装车。
先看不是什么
我们三个人分头看。Nginx 那边 QPS 不高,峰值一分钟 90 多次,跟平时早市差不多,不像被打流量。PHP-FPM 进程没满,Redis 命中率正常,队列也没堆。
那就不是并发。
是隔壁那个定时任务
翻慢查询日志,4 点 55 分开始有一批大查询冒出来,一条跑 8 秒多。是每天凌晨的品类汇总,把 40 多万行明细按品类 GROUP BY 一遍生成日报。平时它 3 点就跑完了,那天上游一份 CSV 到得晚,任务被顺延到 4 点 55 才启动,正好压在 5 点开单头上。
大查询占着 MySQL 的 IO 和连接,开单那些小写入跟着排队,超过前端 8 秒超时就报失败。
当场怎么处理
第一步先把那个汇总任务停了,早市优先。5 点 24 分开单恢复,失败的单让仓库照着重提了 6 笔,其余的其实已经写进去了,只是响应没回来,按订单号核了一遍没重复收。
第二步,任务改时间,从「上游文件到了就跑」改成最晚 3 点 40 必启动,文件没到就跳过并告警,不许往 5 点漂。
第三步,汇总这种只读大查询单独走从库,不碰主库。之前主从是误删那次加的,一直只当备份用,这回真派上用场了。
后来
加了一条检查:所有定时任务的时间点列在一张表里,5 点前后 30 分钟内不许有重活。列出来发现还有两个轻量任务贴着 5 点,也挪了。
那份晚到的 CSV 后来查出来是对方导出脚本卡了 40 分钟。我们在维护清单那栏「外部数据」里补了一行,写到 2026 年了。
1 月 16 号之后早市没再卡。仓库那边照旧 5 点开单,6 点半截单,没人再提那天早上的事。

