4. 作业计划与调度策略
SQL Server Agent 作业的“计划”(Schedule)决定了作业在什么时间、以什么频率被触发。理解计划类型与调度语义,是作业治理中最容易踩坑、也最值得先打牢的一环。很多生产事故并非作业逻辑写错,而是计划配置导致同一作业被重复触发、多个作业在同一时间窗口争抢资源,或者本该错峰执行的作业全部挤在凌晨 2:00。
一、计划类型与调度语义
SQL Server Agent 支持四类计划:一次性计划(One time)、重复执行计划(Recurring,按频率分为每日、每周、每月)、CPU 空闲计划(Start automatically when CPU becomes idle)以及由这些计划组合而成的多计划作业。一个作业可以绑定多个计划,任一计划触发都会启动该作业。
每日计划的核心参数是“每 N 天执行一次”,配合一个具体时间点。例如“每 1 天,02:00:00 执行”,表示每天凌晨 2 点触发。每周计划需要指定星期几(可多选)与每 N 周执行一次,例如“每 1 周的周一、周三、周五 03:00 执行”。每月计划有两种表达方式:按“第 N 天”或按“第 N 个星期几”,例如“每月 1 日 01:00”或“每月最后一个工作日 23:00”。后者在月末对账、月度归档场景中非常实用,因为它能自动适配不同月份的天数差异。
需要特别注意的是,Agent 计划使用的是服务器本地时间,而不是 UTC。如果服务器时区、夏令时(DST)发生切换,计划触发时间会随之偏移。跨时区部署或使用云主机时,应确认实例所在时区,必要时通过调整计划时间或改用外部调度工具来规避。
二、CPU 空闲计划的适用边界
CPU 空闲计划的设计初衷是:当 SQL Server Agent 检测到 CPU 持续空闲(默认空闲定义与持续时间可在 Agent 属性中配置)时,才启动作业。它适合那些“不紧急、可延后、资源消耗大”的维护类任务,例如索引重建、统计信息更新、历史数据清理。
但 CPU 空闲计划在生产环境中往往不可靠:如果服务器长期处于中等以上负载,空闲条件可能永远不满足,作业就一直不执行;反之,如果空闲判定过于宽松,又可能在业务低峰期之外被触发。因此,对于有明确 SLA 要求的作业,不建议依赖 CPU 空闲计划,而应使用固定时间窗口 + 资源调控(如 Resource Governor)来约束影响。
三、计划冲突与并发控制
计划冲突主要有三种形态:
- 同一作业被多个计划同时触发:例如一个作业既有每日 02:00 计划,又有每周一 02:00 计划,周一凌晨就会产生两次触发请求。
- 不同作业在同一时间窗口竞争资源:多个大作业同时启动,导致 CPU、I/O、tempdb 争抢,整体耗时反而拉长。
- 作业执行时间超过计划间隔:例如每 5 分钟执行一次的作业,单次运行需要 8 分钟,就会出现重叠执行。
SQL Server Agent 对同一作业的并发控制,取决于作业的“已启用”状态与历史记录判断。默认情况下,如果上一次执行尚未结束,新的触发请求会被跳过(不排队、不等待),并在作业历史中记录一条“作业已在运行”的信息。这意味着:Agent 本身不会让同一作业并行执行两次,但也不会补跑被跳过的这一次。对于必须“每次都要执行”的场景,这种跳过行为会造成数据缺口,需要额外设计补偿机制。
四、避免作业重叠执行的工程做法
最直接的做法是在作业步骤开头加入“互斥锁”判断。常见实现是使用 sp_getapplock 获取应用锁,获取失败则直接退出本次执行:
DECLARE @rc INT;
EXEC @rc = sp_getapplock
@Resource = 'Job_ETL_DailyLoad',
@LockMode = 'Exclusive',
@LockOwner = 'Session',
@LockTimeout = 0;
IF @rc < 0
BEGIN
PRINT '上一次执行尚未结束,本次跳过。';
RETURN; -- 直接结束作业步骤
END
-- 正常业务逻辑
-- ...
EXEC sp_releaseapplock @Resource = 'Job_ETL_DailyLoad', @LockOwner = 'Session';
另一种做法是使用一张“作业运行状态表”,在作业开始时插入运行标记,结束时更新为完成;若发现存在未完成的运行记录,则跳过或等待。这种方式便于审计和人工干预,但需要额外维护表结构与清理逻辑。
此外,还应从计划设计层面减少冲突:
- 为同一作业只保留一个主计划,避免多计划在同一时间点重叠触发。
- 将大作业错峰排列,例如索引维护 01:00、统计信息更新 03:00、数据归档 05:00,而不是全部放在 02:00。
- 对执行时间波动大的作业,使用“每 N 分钟”计划时预留足够余量,或改为由上游作业完成后触发。
- 在作业属性中设置合理的“执行超时”与“重试次数”,避免失败后无限重试造成堆积。
五、计划配置检查清单
下面这份清单可直接用于日常巡检或上线前评审,字段均围绕 Agent 计划本身:
| 检查项 | 判断标准 | 常见问题 |
|---|---|---|
| 计划类型 | 与作业时效要求匹配 | 用 CPU 空闲计划承载 SLA 作业 |
| 触发时间 | 避开业务高峰与其它大作业 | 多个作业集中在同一分钟 |
| 频率间隔 | 大于作业平均执行时长 | 每 5 分钟执行但单次需 8 分钟 |
| 多计划重叠 | 同一作业无重复触发点 | 每日与每周计划在周一重叠 |
| 并发保护 | 关键作业有互斥或幂等设计 | 依赖默认跳过行为 |
| 时区与 DST | 确认服务器本地时间 | 跨时区部署未调整 |
把计划配置当作作业治理的第一道防线:先确认“什么时候该跑”,再确认“跑的时候会不会撞车”,最后确认“撞车了怎么发现、怎么补”。这三步做完,绝大多数因调度引发的生产问题都能提前拦截。