如果让你设计一个支持千万级订单超时关闭的延时任务系统,你会怎么做?
我会先定目标和容量,再拆核心链路:小规模低精度任务可用数据库按执行时间建立索引并分批抢占;已有消息系统且延迟级别受支持时可用延迟消息;海量近时任务可用分层时间轮降低调度成本。无论调度器如何实现,执行通常至少一次,业务处理必须幂等。 任务表或事件日志是可靠事实源,内存时间轮只是加速索引,进程重启后必须能重新加载未完成任务。
我不会直接画架构图,会先确认目标、规模和一致性要求。比较定时扫描、延迟队列和时间轮,并处理可靠投递、幂等与取消。小规模低精度任务可用数据库按执行时间建立索引并分批抢占;已有消息系统且延迟级别受支持时可用延迟消息;海量近时任务可用分层时间轮降低调度成本。无论调度器如何实现,执行通常至少一次,业务处理必须幂等。 任务表或事件日志是可靠事实源,内存时间轮只是加速索引,进程重启后必须能重新加载未完成任务。
容量有了以后,再把入口、核心处理和数据落点串起来。沿着「创建任务与执行时间 → 持久化并进入时间索引 → 调度器领取到期任务 → 工作者幂等执行 → 重试/取消/对账收口」观察输入、状态与输出,这些阶段都可以从日志、指标或源码里验证。 创建任务与执行时间 任务记录至少包含业务 ID、执行时间、状态、重试次数和幂等键,创建与业务事务需避免双写丢失。 持久化并进入时间索引 短延迟可用时间轮,长延迟可用数据库扫描、Redis ZSet 或分层消息,精度和容量不同。 task 状态表+dueat 索引:持久时间索引。 租约 owner/leaseuntil:重复领取恢复。 我会先确认请求实际走到了哪条路径,再用运行数据验证,不会只看类名或配置猜测。
关键参数要从峰值流量和资源上限反推。是否明确任务规模、延迟精度和最长延迟时间。 能否比较数据库扫描、消息延迟和时间轮。 是否设计任务状态、重复执行、取消与故障恢复。
正常链路之外,还要设计失败补偿和可验证的恢复流程。服务先创建订单事务,提交后再写延迟队列,进程崩溃留下永不关闭的订单。将任务记录与订单放同一事务并由调度器扫描,或用 Outbox 投递延迟消息后才消除窗口。 业务提交与任务创建双写不一致:到期到执行延迟:业务与任务同事务/Outbox。 任务执行超时后租约过期导致并发执行:待执行/重试积压:租约超时可接管。 固定输入和基线 先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「到期到执行延迟」为主基线,记录值应满足「记录活动/稳态基线」;同时保存 到期到执行延迟、待执行/重试积压,使后续变化能够回到同一时间轴比较。
最后再讲扩容、成本和方案边界。方案:更适合的场景:主要收益:代价与边界。 数据库时间索引:任务量中等且要求可靠:事务与查询直观:高频扫描和热点需治理。 Redis ZSet/时间轮:短延迟、高吞吐:调度效率高:持久性、故障恢复需补充。 延迟消息/分层 Topic:已有 MQ 且延迟档位固定:削峰、消费体系成熟:精度、回溯和取消较复杂。 选型至少带上 峰值流量、数据规模、依赖容量、一致性目标和故障预算,并用上面的量化基线验证;