当前位置: 首页 > news >正文

【定时任务频率与延迟任务 / 轮询选型指南】

定时任务频率与延迟任务 / 轮询选型指南

典型场景:借阅逾期(ZSET +@Scheduled)、购书未支付取消(RabbitMQ 延迟消息)、预约爽约(待扩展)


写在前面

业务里常遇到一类问题:某件事要在未来某个时间点发生(订单超时取消、借阅到期标记逾期、预约结束自动完成等)。

实现方式通常两类:

模式代表
延迟任务事件发生时预约一次执行(RabbitMQx-delayed-message、Redis 键 TTL + 回调等)
轮询定时醒来查「有没有到期的」(@Scheduled+ SQL / Redis ZSET)

本文说明:Scheduled 任务频率怎么设两种模式怎么选,并结合本项目已有实现举例。


一、Scheduled 任务频率怎么设?

1.1 核心原则:频率 ≤ 业务容忍误差

先问:晚处理一会儿,业务能不能接受?

业务场景典型容忍误差建议频率
借阅逾期标记1~5 分钟poll-interval-ms: 60000300000
未支付订单取消秒~分钟级延迟 MQ 精确触发,或 30s~1min 轮询
借阅申请超时未取(APPLIED→CANCELLED)小时级每小时或每 30 分钟
预约时段结束 / 爽约分钟级1~5 分钟
报表、对账小时~天级每小时 / 每天凌晨
缓存预热、统计不敏感5~30 分钟

不要把频率设得比业务需要的精度更高:越快 = 空跑越多、DB/Redis 压力越大,收益却有限。


1.2 用「批次 + 频率」估算能否消化峰值

本项目借阅逾期配置(application.yaml):

bookstore:borrow:overdue:enabled:truepoll-interval-ms:60000# 每次 tick 间隔 60 秒(fixedDelay)batch-size:100# Lua / SQL 每批最多 100 条

单次 tick 的处理能力(当前实现每 tick 只 Lua pop 一次):

吞吐 ≈ batchSize / pollInterval 默认:100 条 / 60 秒 ≈ 100 条/分钟

若某时刻500 条同时到期

  • 仅靠 Lua pop:约 5 个 tick ≈5 分钟处理完(fixedDelay 从上次结束计时)
  • 对账 SQLreconcileOverdueBatch可在同一 tick 内多批消化部分 backlog

估算公式

pollInterval ≤ 可接受最大延迟 / ceil(峰值条数 / batchSize)

示例:要求 2 分钟内处理完 500 条、batchSize=100
→ 至少需要 5 批 →pollInterval约 ≤ 24 秒,或同一 tick 内循环 pop 直到为空


1.3 为什么需要 batchSize(批次大小)?

BorrowOverdueScheduler与 Lua 脚本borrow_due_pop.lua均使用batch-size限制单次处理量:

原因说明
Redis Lua 阻塞Lua 执行期间 Redis 单线程阻塞;一次 pop 上万条会卡顿
定时任务耗时N 条 = N 次 DB UPDATE,过长会拖住 Scheduler 线程
削峰大量due_at相同时,分批摊平
失败隔离单批失败影响面小

1.4 fixedDelay vs fixedRate

类型行为推荐
fixedDelay上次执行结束后再等待 N 毫秒✅ 本项目BorrowOverdueScheduler使用
fixedRate每 N 毫秒触发一次,不管上次是否跑完易任务堆叠,慎用
@Scheduled(fixedDelayString="${bookstore.borrow.overdue.poll-interval-ms:60000}")publicvoidprocessOverdue(){...}

若单次处理 30 秒、fixedDelay=60s → 实际约每 90 秒一轮;fixedRate=60s 则可能重叠执行。


1.5 环境与参数建议

环境poll-interval-msbatch-size说明
本地联调10000(10 秒)50~100配合手动改due_at到过去
生产 / 答辩60000300000100借阅量小,1~5 分钟足够
压测 / 大量到期30000200或 tick 内循环 pop

其他实践:

  • 参数放application.yaml,支持环境变量覆盖
  • 提供enabled: false开关(本项目已支持)
  • 多个 Scheduler错峰cron,避免整点同时打 DB
  • 日志记录每批marked/reconciled条数与耗时,便于调参

二、延迟任务 vs 轮询:两种触发模型

轮询

定时醒来

查有没有到期

有则处理

延迟任务

事件发生

预约 N 秒后执行

到点触发一次

2.1 对比总表

维度延迟任务(MQ / 延迟队列)轮询(@Scheduled / ZSET + Scheduler)
触发精度单事件较精确(秒级)取决于间隔(常见分钟级)
延迟时长短延迟(分钟)合适;长延迟(30 天)需评估 Broker长短均可,只判断「是否到期」
实现复杂度MQ、延迟插件、DLQ、消费幂等Scheduler + SQL / Redis 即可
空闲时资源无到期事件则不消费定时空跑(ZSET 可减轻无效 SQL)
批量同时到期每单一条消息,量大时消息多一次批量取,天然适合
改期(续借)难(需取消/替换已发消息)易(改due_at+ ZADD 覆盖)
多实例Consumer 竞争 + 业务幂等Lua 原子 pop / 条件 UPDATE
故障恢复死信队列 + 定时对账下次轮询 + SQL 对账兜底

2.2 决策树

需要秒级精确触发? └─ 是 → 倾向延迟 MQ(如未支付 15 分钟取消) 延迟是「每单不同、且很长」(如 30 天)? └─ 是 → 倾向轮询 / ZSET;慎用长延迟 MQ 大量订单会在同一时刻到期? └─ 是 → 倾向轮询 + 批量(ZSET / 条件 UPDATE) 到期后要触发很多下游(通知、罚息、审计)? └─ 是 → 延迟 MQ,或「轮询发现 + 发 MQ 异步」 只是改个状态、量不大、要求简单? └─ 是 → @Scheduled + 条件 UPDATE 最省事 已有 MQ 且希望技术栈统一(短延迟场景)? └─ 是 → 可优先延迟 MQ

三、本项目两个典型案例

3.1 购书未支付取消 → 延迟 MQ ✅

实现TradeMqProducer+x-delayed-message+TradeOrderTimeoutConsumer

bookstore:trade:unpaid-cancel-delay-minutes:15# unpaid-cancel-delay-ms: 60000 # 联调可改为 1 分钟

流程

创建订单 → 发 delay=15min 的消息 → 到点 Consumer 取消订单

为何选延迟任务

因素说明
延迟固定、较短15 分钟,Broker 友好
需要相对精确超时后应尽快释放库存
每单一条消息量级可控
基础设施已有与秒杀 MQ 同一套 RabbitMQ

3.2 借阅逾期 → ZSET + 轮询 ✅

实现BorrowDueRedisService+borrow_due_pop.lua+BorrowOverdueScheduler

bookstore:borrow:overdue:enabled:truepoll-interval-ms:60000batch-size:100

流程

confirm → ZADD(due_at) Scheduler 每 60s → Lua 原子 pop → UPDATE OVERDUE └─ reconcileOverdueBatch SQL 对账兜底 return → ZREM

为何选轮询(+ ZSET)

因素说明
延迟长且每单不同如 30 天,不宜每单占一条长延迟 MQ
精度要求低分钟级标记 OVERDUE 即可
可能批量到期同一批 confirm 的due_at接近
续借改期ZADD 覆盖 score 即可
多实例Lua 原子 pop 避免重复领任务

若强行用延迟 MQ:30 天 × 大量借阅 = 消息长期堆积、续借难撤消息、Broker 长延迟可靠性需单独验证。


3.3 对比小结

模块选型延迟特征精度
购书超时取消延迟 MQ固定 15 分钟较高
借阅逾期ZSET + 轮询每单不同,天级分钟级
借阅 APPLIED 超时未取(规划)定时 SQL固定 48 小时小时级
预约爽约(规划)定时 SQL时段相关分钟级

结论:两个模块选型不同,是按场景匹配,不是随意混搭。


四、BorrowOverdueScheduler 逻辑速览

便于理解「轮询 + ZSET」如何与频率配置配合:

@Scheduled(fixedDelayString="${bookstore.borrow.overdue.poll-interval-ms:60000}")publicvoidprocessOverdue(){// 1. Lua:原子弹出 score <= now 的 orderId(最多 batchSize 条)List<Long>orderIds=borrowDueRedisService.popDueOrderIds(nowEpoch,batchSize);// 2. 同步 DB:BORROWED → OVERDUE(条件 UPDATE,幂等)for(LongorderId:orderIds){borrowOrderRepository.updateToOverdue(orderId);}// 3. SQL 对账:ZADD 失败 / 历史数据未入 ZSET 的补漏reconcileOverdueBatch(batchSize);}
步骤作用
Lua pop多实例下原子领任务,避免重复读 ZSET
同步 UPDATE不依赖 MQ,借还闭环内完成状态变更
reconcileRedis 与 MySQL 不一致时的兜底

五、常见组合模式(生产实践)

模式组成适用
纯轮询@Scheduled+ 条件 SQL小项目、只改状态
纯延迟 MQ创建时发延迟消息短延迟、单事件、高精度
ZSET + 轮询 + SQL 对账本项目借阅逾期长延迟、批量到期、要防多实例
延迟 MQ + 定时对账MQ 主路径 + 每日 SQL 补漏对可靠性要求高
轮询 + 发 MQScheduler 只负责发现,通知走 MQ状态同步与通知解耦

六、选型速查表

你的需求推荐
15 分钟未支付取消延迟 MQ(已实现)
30 天借阅逾期ZSET + 轮询(1~5 分钟)(已实现)
只展示「是否逾期」、不改库查询懒判断,无需 Scheduler
毕设求最简单定时 SQL 条件 UPDATE,无需 ZSET
到期要发短信 / 站内信在 OVERDUE 后同步或异步发 MQ(规划 topicborrow.overdue
秒级触发 + 已有 MQ短延迟用 MQ;长延迟用轮询

七、面试 / 答辩参考话术

定时任务频率不是越快越好,而是看业务容忍误差。我们用fixedDelay避免任务堆叠,用batchSize控制单次 Redis Lua 和 DB 压力。

延迟任务适合「事件发生时就知道何时触发、延迟较短」的场景,比如购书 15 分钟未支付取消,用 RabbitMQ 延迟交换机精确触发。

轮询适合「到期时间分散、延迟很长、可能批量到期」的场景,比如借阅 30 天逾期,用 Redis ZSET 索引 + Scheduler 每分钟 pop,同步 UPDATE MySQL,并用 SQL 对账兜底。

两者可以组合:主路径 + 对账,保证最终一致。


http://www.cnnetsun.cn/news/3299731.html

相关文章:

  • 从传统三层到CLOS:数据中心网络架构演进中的3个关键设计范式转变
  • AI 最核心的 15 个概念
  • 如何快速禁用iPhone过热保护:thermalmonitordDisabler终极性能优化指南
  • WinDiskWriter:macOS上最强大的Windows启动盘制作神器 [特殊字符]
  • 自动驾驶下半场:从功能实现到风险可控的工程化落地
  • PyCharm 远程训练 3 大中断问题:网络、睡眠与实例释放的预防方案
  • 从 MVVM 到 MVI:为什么说 MVVM 的 UI 状态像“网”,而 MVI 像“一条线”?
  • IUV-5G 24.6 全网仿真:NSA Option3X 组网下 3 类常见链路故障排查指南
  • 安全帽国标 GB/T 2812-2006 测试实战:从侧向刚性到耐穿刺的5项关键指标复现
  • UnityPy实战指南:Python解析与修改Unity游戏资源
  • VC++ 6.0 与 Visual Studio 2022 项目迁移:5个关键步骤与代码适配
  • Ubuntu 22.04 Git 服务器搭建实战:5步完成SSH证书登录与裸仓库初始化
  • OpenPLC Editor v4 Modbus 配置
  • 活期存款账户全生命周期技术实现:从开户到销户的12个关键交易节点剖析
  • 8.15.智能宠物投喂-WiFi+水泵-基于STM32单片机物联网设计【硬件+APP+云平台】
  • 为什么epkg-factory是OpenEuler生态的关键工具?深度解析其核心价值
  • 一个大专生的程序13年
  • Windsurf IDE与GitHub Copilot协同失效真相:底层AST解析器兼容性漏洞及临时绕过方案(紧急热修复版)
  • 人生本年可能性的庖丁解牛
  • BUUCTF [UTCTF2020]basic-re
  • 大话批量重命名工具:Python实现详解
  • Git 日志排查指南:5个高级参数定位提交历史与问题根因
  • 新手如何规划后端技术栈的学习路线
  • Scrapy 是一个用 Python 编写的高性能、可扩展的开源网络爬虫框架
  • Cursor AI终端智能提示实战手册(2024最新版):覆盖Git/Docker/Shell/Python/Node.js五大生态的78条黄金命令模板
  • ansible常用模块详解
  • HTML5 Audio API实战:为原创手书项目集成背景音乐控制
  • STM32+TMC7300有刷电机驱动方案与PID控制优化
  • 私有化即时通讯系统部署架构:单机、分布式、跨地域怎么选
  • VBA 与 Python openpyxl 批量转换 TXT:5 个文件场景下的效率与灵活性评测