简介《Control-M作业配置手册》是一份面向企业调度平台Control-MCTM的作业定义与配置详解适合初次接触CTM的运维人员与批处理开发工程师也可作为有经验用户的速查参考。内容从CONTROL-M/EM与SERVER架构入手系统讲解作业定义库、AJF自动作业流、Smart Table/Sub Table的定义方法并通过三个完整实例演示作业调度、依赖关系、监控告警及最佳实践同时归纳了作业定义过程中需考虑的时间、资源、依赖等关键因素。涵盖基础概念到高级工作流设计的全链路知识可帮助读者快速掌握作业执行方式与定义要点减少配置错误和排障时间。压缩包内共1个doc文档大小3.05MB图文配合、目录清晰从概览、作业定义工具到实例逐步深入。该手册已有逾7.4万人次浏览学习适合刚上手CTM的读者边学边查。1. Control-M 作业配置到底在解决什么问题先分清“脚本能跑”和“作业能调度”如果你刚接手批量调度平台第一次打开 Control-M 控制台时最容易犯的错是把“配置作业”当成“写脚本”。脚本能不能跑只是第一步真正麻烦的是另外三件事谁先跑、谁在后边等、这一天到底该不该跑。Control-M 作业配置要解决的问题就是把一批彼此有先后、有依赖、有日期规则的脚本变成可追踪、可重跑、可回滚的调度流程。这个方向不挑业务日终报表、数据抽取、文件交换、批量加工核心套路都一样。你不需要先背下一堆菜单只需要先理解“作业 - 作业流 - 日历”三层模型。下面按建单作业、组作业流、配日历、传参数、排故障的顺序讲最后落在配置治理和回滚习惯上。新人能照着搭出第一个可跑通的作业已经维护了很多作业的人也能在这里对照检查那些反复出现的“空跑、重复跑、日期跑错”的问题。2. 从单个作业到作业流先把最小可跑通的调度模型搭起来2.1 作业、作业流、日历三者边界先别混Control-M 里的最小执行单位是“作业”它至少包含作业名、运行用户、命令路径、归属目录以及一串调度条件。作业本身不一定要放进作业流里一个不带条件的作业可以被手工运行但生产环境几乎不这么做因为“跑一次”和“每天稳定跑一次”是两码事。作业流解决的是顺序问题A 成功之后 B 才启动B 失败之后是补跑还是告警C 和 D 能不能并发。需要提醒的是这里说的作业流不是工作流引擎里那种自由态模型而是带调度语义的执行链。每个作业在完成时会生成成功或失败状态后续作业依靠这些状态决定是否启动、等待多久、超时后怎么处理。日历解决的是“今天到底跑不跑”。如果你不给作业配日历调度平台不会帮你猜一个执行日期。作业流放的日期组合通常来自日历表而不是作业内部的时间判断。很多配置混乱的配置都出在同一个地方把“哪天跑”写在日历里又把“星期几”写在作业命令里最后谁都说了算谁都说不清。建议把边界切成三块哪天跑交给日历几点跑交给调度时间跑完干什么交给作业顺序和条件。2.2 用 GUI 或自动化 API 建第一个作业最小字段与操作步骤先建一个最简模型验证你的脚本能被调度平台正常拉起。常见操作路径是登录 Control-M Web 控制台后新建一个文件夹再在文件夹里加入作业。文件夹在生产里建议按业务域命名例如 DAILY_LOAD、MONTH_REPORT避免两年后看到一百多个作业却不知道属于哪条链路。搭建最小模型按五步走新建文件夹命名如 DAILY_LOAD。在文件夹里新建作业作业类型选 Command。填写作业名、命令、运行用户、所属文件夹。先不配置任何条件、日历直接用手工运行验证脚本。在运行日志里确认实际命令、返回码、输出内容。作业字段里最关键的几个值如下表字段推荐写法说明作业名CUST_LOAD_DAILY尽量体现业务和周期别用 test123命令/apps/batch/load_customer.sh写绝对路径不要写 cd /dir; sh xxx运行用户batch_user使用批处理专用账号不用个人账号作业类型Command最通用也最容易排查时间02:30尽量和同链路其他作业错开命令最好别写成cd /tmp sh run.sh调度平台拉起作业时的工作目录不一定是你终端里的目录。下面是一个脚本示例重点在于脚本本身要能接收参数、要具备幂等性#!/usr/bin/env bash # 最小可跑通的作业脚本带业务日期参数且重复执行不覆盖 set -euo pipefail # 参数一业务日期由调度平台传入而不是在脚本里取 date PROC_DATE${1:-$(date %Y%m%d)} BATCH_DIR/data/batch/${PROC_DATE} LOCK_FILE${BATCH_DIR}/.job.lock mkdir -p ${BATCH_DIR} if [ -f ${LOCK_FILE} ]; then echo already processed: ${PROC_DATE} exit 0 fi # 这里放你的实际业务逻辑 echo start load customer, date${PROC_DATE} # 业务处理完成后留下完成标记 echo ${PROC_DATE} ${LOCK_FILE}这段脚本的关键在最后一层它把“是否已经处理过”的判断放到应用侧。调度平台负责什么时候拉起作业应用侧负责重复拉起时不要重复写数据。PROC_DATE这个参数等第 4 章讲传参时再展开现在你先理解一个能被调度平台反复拉起、反复重跑而不会搞坏数据的脚本才适合挂进生产作业流。2.3 作业排序与条件依赖不靠“写成一行”而是靠“前驱状态”如果业务里已经有三个脚本它们是按顺序执行的最简单的做法是写一个大脚本从头跑到尾但这样的缺点是中间某一步断了整段重跑且看不出断在哪一步。Control-M 的推荐做法是把三个脚本拆成三个作业用作业状态连接起来。常见依赖配置如下表依赖类型配置方式典型场景前驱成功后执行作业 B 的条件设置为“作业 A 成功”抽取完成后才能装载前驱失败后分支作业 B 失败后触发告警或执行补偿作业装载失败后清理临时表文件到达后执行使用文件触发作业等待指定路径外部系统传到文件后开始处理时间窗口从 02:30 开始等等到 03:30 未满足就超时防止无限期等待在界面里不要手工去记复杂的条件表达式。选中后续作业在条件选项卡里选择条件类型为“作业状态”再指定前驱作业和期望的状态即可。这样配置的好处是运行监控页能直接看到“B 正在等待 A 成功”这样的提示排障时不需要翻代码。我一般会建议把“失败后做什么”也配在作业上而不是只靠报警。例如 C 装载数据库失败后自动执行一个清理作业把本次写入的半成品数据回滚然后再发通知。这样值班人员收到消息时看到的是已经被处理过的现场而不是一堆脏数据。2.4 作业流的最小实践独立运行、试运行与排错作业流配好后不要直接在生产里点“强制运行”。更稳的做法是复制出一个测试文件夹在测试副本里把输出路径、目标表都改成测试区确认条件链完整后再手工运行整个作业流。手工运行和按日历运行有差别。手工运行只是把作业拉到当前日期执行它不会绕过依赖条件也不会改变日历判断。换话说如果作业挂在等待条件上手工运行也不会立刻启动这能帮助你保留真实依赖关系。试运行状态下建议重点关注四个位置作业状态等待、运行中、完成 OK、异常退出、强制终止。运行日志看命令在调度平台侧展开后的真实内容。返回码看脚本执行结果是否被平台正确识别。条件列表看当前作业到底在等哪个前驱。排查时有一个好习惯先看日志里展开后的命令再回查脚本本身。很多作业失败不是因为逻辑写错而是命令字段里的参数、引号、路径在平台侧被解析成了另一个样子。这一点也是后面传参和避坑章节的重点。3. 生产日历把跑批节奏和业务日期焊死在配置里3.1 规则日历、工作日偏移与月末场景怎么选日历是 Control-M 作业配置里最容易偷懒、也最容易出事的一层。很多人觉得日历就是“每周跑一次”“每天跑一次”的开关实际上生产日历要处理的是月末、季末、法定节假日、行业特殊日。规则日历适合周期固定的场景。比如每天跑、周一到周五跑、每月 5 号跑、每月最后一个工作日跑。特殊日历适合手工勾选日期的场景比如大促日、关账日、每年临时加班的星期六。建议不要把所有日期规则都堆在一个日历里而是建一个“通用工作日历”作为基底再用特殊日表做加减法。月末场景是最典型的翻车点。假设业务要求在每月最后一天完成对账但最后一天恰好是星期六平台不会自动把作业顺延到下一个工作日除非你把它配置成“最后一个工作日”。如果业务还要求“最后一个工作日之后一天再跑下一个作业”要注意区分自然日偏移和业务日偏移。写“1 个业务日”和写“1 天”结果完全不同尤其是跨周五的情况下。3.2 处理日期和运行日期为什么作业里不要拿系统日期批量作业里有两类日期很容易混一是“当前调度系统认为今天是哪一天”二是“这笔数据实际属于哪一天”。后者在行业里叫业务日期或处理日期。举个例子日终批在凌晨 00:30 运行它处理的是前一天的交易数据。如果脚本里写date %Y%m%d跨月时 1 号凌晨算出来的日期是 1 号但你要处理的是上月 31 号的数据。文件名、表分区、对账日期都会错一位。还有更隐蔽的情况假期后第一个工作日系统把假期没跑的数据一次性补跑这时服务器日期已经跳到了假期后脚本如果只认系统日期根本不知道自己在补哪天。正确做法是把业务日期作为参数由调度平台在配置里传下去。你在日历上看到的“调度日期”和实际命令里要用的“处理日期”可以不同这个偏移量应该在作业配置里显式表达而不是让脚本去猜。3.3 日期变量与替换最小参数配置把日期穿进作业命令最可靠的方式是使用作业级变量。在作业属性里新建一个自定义变量比如 PROC_DATE类型选日期格式设为 YYYYMMDD然后在命令字段里引用它。界面表单里填入的调度日期如果它也是用同一来源那么命令中引用这个变量就不会出现脚本里的系统日期错误。命令字段的写法类似这样/apps/batch/report_daily.sh ${PROC_DATE}注意参数加了双引号。如果变量包含空格或者为空不加引号会被 shell 拆成多个参数脚本拿到的就不是一个完整日期。日志里如果看到命令变成/apps/batch/report_daily.sh 2025 01 01这种样式基本可以断定是引号问题。另一个建议是不要把变量替换结果再拼进路径里去做判断比如if [ ${PROC_DATE} 20250101 ]这种逻辑放在调度侧很脆迁移日期环境后很容易遗忘。在作业运行后到运行日志里找到“展开命令”或“实际命令”确认变量是否被替换成你预期的值。这一步虽然很小却能挡掉后续很多“明明脚本没问题调度跑出来却不对”的疑难杂症。3.4 需要调整的三个日历属性过期处理、假日补跑、提前预推送日历配置不是“放行或不放行”这么简单。还有三个属性生产上要专门过一遍。第一个是过期处理。作业如果因为前一天的调度故障没有运行第二天平台应该跳过期作业还是立即补跑不同业务要求不同但默认值不一定符合业务预期。每类作业都要明确写下来避免事后扯“为什么昨天没人通知”。第二个是假日补跑。法定节假日系统是不放行的但很多报表在假日次日补跑时业务日期要回填到假日当天。此时不能简单把日历改成每日否则会把正常工作日也跑一遍正确的做法是维护一个“特殊放行日”清单并在补跑时手动指定业务日期参数。第三个是提前预推送。有些下游系统需要提前一小时收到数据但日历触发时间又希望保持不变。这个场景不要在作业命令里做 sleep应该使用日历偏移或发布条件让下游作业先启动等待。下面是一个日历参数对照表场景建议配置不要做的事每月最后一天规则日历 最后一个工作日写死每月 30 或 31 号周末不跑通用工作日历在脚本里判断星期几假期不跑特殊日历排除靠人工临时改作业月末加跑增加特殊放行日修改通用日历4. 脚本接入与参数传递从“能跑”到“能正确传参、能走分支”4.1 作业类型选型Command、文件触发、作业流调用Control-M 里作业类型比较多但平时最常用的是三类。Command 作业就是执行一条命令或一个脚本适合百分之八十以上的场景文件触发作业用来等待某个文件出现或消失适合外部系统之间交换文件的场景作业流调用则是把一个公共流程包成子流程供多个主流程复用。三类作业怎么选有一个简单的判断标准如果作业需要主动轮询一个文件是否存在优先考虑文件触发如果多个业务流都要执行同一套数据清洗逻辑把清洗做成可调用的作业流分支如果只是按顺序跑脚本直接用 Command 作业别绕弯子。文件触发作业特别适合“上游文件凌晨传过来传完才能抽数”的场景。用 Command 去定时检查文件路径会带来大量空跑文件触发作业由 Agent 端去监听文件出现文件一到再启动后续作业不仅省资源依赖关系也清楚。4.2 命令写法与运行用户路径、环境变量、umask作业命令的写法看起来简单但生产事故有不少就出在这里。第一命令必须写绝对路径。第二运行用户要固定不要让同一个作业今天用 batch_user 明天用 root。第三脚本内要自己加载环境变量不要依赖调度平台帮你初始化 shell 环境。很多批处理作业喜欢在命令字段里做一堆拼接比如/usr/bin/bash /apps/run.sh /logs/run_$(date %Y%m%d).log 21。这种写法的问题在于日志文件名里的日期来自系统日期而不是业务日期跨月和补跑时日志会写到错误文件排障时找不到。更稳的命令写法是只保留业务连参数其他环境变量交给脚本处理/usr/bin/env bash /apps/batch/load_customer.sh ${PROC_DATE}这里显式用了/usr/bin/env bash避免不同服务器默认 shell 不同导致语法不兼容。日志重定向放在平台侧或脚本内都可以但一致性更重要要么所有作业都在脚本里重定向要么都交给平台收集。运行用户的环境是个大坑。经常出现终端手动跑没问题、调度跑却报找不到命令原因就是调度用户没加载/etc/profile或用户.bash_profile。建议脚本开头通过source /etc/batch_env.sh统一加载 PATH、PYTHONPATH、LD_LIBRARY_PATH而不是依赖交互式 shell。4.3 输出条件与文件条件的运用让后续作业判断“是否继续”作业成功执行并不代表数据正确。经常有场景抽数作业返回码为 0但生成的数据文件是空的后续装载作业照样跑最后报表对不上账。为了挡掉这种问题用一条文件条件等着检查结果文件。具体做法是在抽数作业之后插入一个文件触发作业等待目标文件出现并且指定最小文件大小或非空判断。文件触发作业能等多久也要设置超时时间。如果文件一直没来作业进入异常状态然后触发告警。这样后续装载作业不会因为文件不存在而启动一个空流程。另一种常见场景是失败分支。比如数据库装载作业失败后先执行一个清理作业把已经写入的半成品表清空再通知值班人员。在作业配置里B 作业的后续可以同时挂两条分支成功走“通知完成”失败走“清理和告警”。这样值班看到的状态是“已被处理”而不是一条孤零零的失败红色。4.4 重试、通知与恢复选项配置失误更早被发现自动重试是个好东西但不要对所有作业一视同仁。数据装载类作业如果失败首先要确认是不是已经写入了部分数据这时盲目重试可能产生重复主键或重复金额。正确配置是区分业务幂等性幂等作业可以自动重试两三次非幂等作业失败后只告警、不自动重试。通知也需要分级。失败的作业太多时如果每个都发一条全组邮件反而淹没了关键信息。更实用的做法是关键链路作业失败后发即时通知非关键作业只在超时未恢复时升级通知。还有一点通知配置里要带上作业名、运行日期、失败原因摘要否则值班人员收到消息后还得登录平台查一遍。恢复选项上建议统一约定一个“失败后操作”模板。例如数据库装载作业失败后暂停后续作业、保留现场、通知数据组报表生成作业失败后直接重试两次、再失败则通知主管。模板不要写得太多否则维护成本会盖过收益。5. Control-M 作业配置避坑记录五个最容易翻车的点5.1 现象作业一直停在“等待条件”没有死也没跑这是最常看到的故障状态作业不报错、不运行一直挂在等待中。出现这种现象先看作业的条件列表而不是去查脚本。原因通常有三种一是设置了前驱成功条件但前驱作业没有在当前调度日期放行所以条件一直未满足二是条件名写错前驱作业实际生成的状态和后续作业等待的状态对不上三是使用了跨文件夹条件但跨文件夹条件没有在系统参数里激活或被过滤掉。解决时要分两步。第一步在监控页里展开作业的“等待条件”确认它到底在等谁第二步用排除法临时禁用该条件手工运行一次如果作业能跑通说明脚本没问题问题一定出在条件关系上。把条件一个个加上去直到复现出卡住那一层就能定位到具体的前驱或条件名。5.2 现象假期后上班批处理把同一批数据重复算了一遍假期结束后的第一个工作日常常是事故高发时段。作业没有按日历跳过假期导致假期当天遗留作业和假期后正常作业一起跑数据文件互相覆盖数据库表重复写。原因不外乎两个方向。日历侧生产者没有提前维护假期特殊日历法定假日仍是放行日脚本侧作业命令里用了系统日期补跑时数据落到了错误的业务日期分区后续作业基于错误分区重复处理。解决的办法是节日来临前先在日历表里把所有假期标记为非放行日补跑时不要一次性把所有假期作业全部拉起来要在作业流级别查看依赖按业务日期逐个手工指定。补跑前还要检查命令日志中的处理日期确保脚本拿到的参数确实指向假期当天而不是系统当前日期。5.3 现象参数变量传过去被截断或变成空串比较典型的是命令字段里写了变量日志里却看不到完整值。例如日期变量传过去变成202501少了两天又比如变量值包含空格传给脚本时被拆成两个位置参数后面所有参数整体错位。原因通常是变量类型定义不对。比如在变量定义里把日期类型设成了数字前导 0 被去掉或者是命令字段没有加引号变量展开后包含空格被 shell 重新分词还有一种情况是变量名拼写错误平台没有替换直接把原样字符串传给了脚本。解决时要养成一个习惯每次作业运行后第一时间打开日志里展开后的实际命令。确认引号是否存在、变量是否换成了预期值。不要只盯着脚本输出看调度平台传递进命令字段的内容才是脚本真正收到的东西。参数问题排查到具体位置后通常改一处引号或修一处变量类型就能解决但前置条件是你要先看展开日志。5.4 现象重跑作业流时重复执行了同一批任务故障重跑本身是为了恢复数据但经常反被重跑坑一把作业流从第一个作业开始全部重跑把已经成功执行过的任务又执行了一遍数据库里出现重复数据。原因往往是重跑时选择了“重新运行整个作业流”而不是“从失败作业继续”。只要前几个作业已经成功写入了数据重跑就会造成脏数据。另一个更深层的原因是应用侧没有批次幂等设计同一个业务日期可以被重复写入。解决方式分两层。调度层配置时明确重跑策略只有第一个作业从头开始后续作业如果没有失败就跳过。应用层则要加上批次唯一键或完成标记例如数据库表里使用业务日期加作业类型作为唯一约束脚本在执行前先检查结果是否存在。把调度重跑和应用幂等同时做好才不会一到补跑就提心吊胆。5.5 现象并发高峰作业排队明明集群资源还有余量凌晨 2 点整有一大批作业同时启动控制台上看到作业在等待 Agent 执行但服务器 CPU 和内存都不高。这个问题常见于配置初期没有做运行时间平滑。原因有两个角度。一是所有作业都配了完全一致的开始时间Agent 端线程池被短时占满后面作业只能排队二是作业中设置了资源锁资源名相同但最大并发设置过小导致不相关的作业互相锁死。解决方式是在日历或作业流级别做错峰同一台机器上运行时间建议错开 5 到 15 分钟尤其是高压力任务。资源锁的属性键可以用业务线做隔离不要一个大资源名管住整个集群。如果 Agent 数量充足还要检查单台 Agent 的并发线程数配置别让 Agent 成为隐形瓶颈。6. 把作业配置当代码治理模板、导出、回滚和一点使用习惯6.1 配置模板与基线比“记住按钮”可靠作业配置多了以后最大的问题不是某一个作业怎么配而是所有作业是不是按同一套规范配。如果十个作业用十种写法后续排障和交接都会很难。建议把作业配置模板化。日常操作时不要每次都从空白作业开始建而是复制一个“标准作业模板”再修改作业名、命令、日历和通知。复制模板这个动作比新建更安全因为模板里已经预设好运行用户、超时时间、日志策略这些容易漏的字段。每周或每次发布前可以导出作业定义做一个配置基线。比对工具不一定要多高级只要能看出“哪个作业的时间被改了、哪个日历被换掉了”就行。这样做的好处是作业被谁改了配置什么时候改的一目了然。不要相信某个人说“我只改了一个参数”配置基线会替你记住。6.2 回滚与重跑的基本动作回滚不是把作业定义改回旧版本就结束生产上要按三步走。第一步是暂停后续作业。发现当前作业流异常时先暂停下游作业避免错误数据继续向后扩散这一步比改配置更重要。第二步是修正参数或脚本用导出的旧配置对照差异确认是日历问题、参数问题还是依赖问题。第三步是从断点重跑。重跑时不要动已经成功的作业只重跑失败及后续未完成的作业。我现在配置任何作业时都会刻意检查四件事作业名是否能看出业务和周期命令是否带绝对路径和明确参数日历是否引用共享日历而不是写死日期重跑策略是否与应用幂等性一致。这四件事看起来琐碎但几乎能规避百分之八十的批处理故障。这一行做得久了你会发现 Control-M 配置并不玄真正让人头疼的永远是那些“当时以为没问题”的细节。把每个作业当成长期维护的代码来写给后人留清楚字段关系和日期处理逻辑回报远超当时的几分钟操作希望帮到你。本文还有配套的精品资源点击获取