relay 每秒查一次 select * from outbox where status=0,表里有 400 万行,主库 CPU 从 30% 涨到 90%,慢查询里这条每次扫 40 万行。日志里能对上时间点,这种反而好排。
status 上没有索引,每轮都是全表扫描。已投递的记录一直留在表里,表越来越大,单次扫描的代价跟着涨。这一点先确认清楚,后面就不用反复试。
ALTER TABLE outbox ADD INDEX idx_status_id (status, id); SELECT id, payload FROM outbox WHERE status = 0 AND id > 0 ORDER BY id LIMIT 500;
「outbox 轮询扫表」排查到最后往往是很小的一处差异,前面那些复杂猜测可以先放一放。先做最小复现,能稳定跑出来的问题,排查成本会低一个数量级。
别只看报错的最后一行,往上看几行,真正的起因通常在上面。同类问题如果隔一段时间又出现,多半是某个定时任务或重启流程把改动冲掉了。
处理上按这个顺序:给 status 和 id 建联合索引 让条件走索引;投递成功的记录搬到归档表 主表只留最近三天;轮询间隔从 1 秒放宽到 3 秒 用批量取 500 条抵掉延迟;翻页改用 id 游标 不要 offset。
结果对不对很好判断,慢查询里这条的扫描行数降到 500 以内 主库 CPU 回到 40% 以下 投递延迟没有明显变化。
