作业流程图
作业运行状态对照
ode | description | 备注 |
1010 | Waiting for scheduling | 作业已提交,准备调度 |
1011 | Waiting for cluster resource | 等待作业资源 |
1012 | Waiting for concurrent task slot | 等待并发执行资源 |
1013 | Waiting for data replication | 等待数据复制 |
1014 | Waiting for execution slot | 等待执行资源 |
1015 | Waiting for cleaning up of previous task attempt | 等待清理执行历史完成 |
1020 | Waiting for execution | 等待作业处理 |
1030 | Preparing for execution | 准备进行作业处理 |
1032 | Task is executing | 作业处理中 |
1040 | Job has been submitted | 作业提交计算集群 |
1041 | Offline Job Waiting for running | Fuxi作业等待执行 |
1042 | Offline Job is running | Fuxi作业执行中 |
1044 | Offline Job is failed | Fuxi作业执行失败 |
1045 | Offline Job is succeed | Fuxi作业执行成功 |
1046 | Offline Job is cancelled by fuxi | Fuxi作业被取消 |
1050 | Task rerun | 重新执行() |
1051 | Online Job Waiting for running | 作业以ServiceMode方式等待执行 |
1052 | Online Job is running | 作业以ServiceMode方式执行中 |
1054 | Online Job is failed | 作业以ServiceMode方式执行失败 |
1055 | Online Job is succeed | 作业以ServiceMode方式执行成功 |
1056 | Online Job is cancelled by fuxi | 作业被取消 |
1060 | Task key-path executing finished | 作业关键路径处理完成 |
1062 | Task key-path is finished Task | 关键路径处理完成 |
1065 | Instance key-path is finished Instance | 关键路径处理完成 |
1070 | Task execution is finished | 作业处理完成 |
1080 | Instance execution is finished | Instance处理完成 |
1090 | Execution failed | 执行失败 |
1210 | SQLTask is initializing | SQL作业初始化中 |
1220 | SQLTask is compiling query | SQL作业编译中 |
1230 | SQLTask is optimizing query | SQL作业优化中 |
1235 | SQLTask is splitting data sources | SQL作业优化中 |
1240 | SQLTask is generating execution plan | SQL作业生成执行计划中 |
1250 | SQLTask is running the plan on fuxi | SQL作业调度执行中 |
1260 | SQLTask is update meta information | SQL作业更新元数据信息 |
1270 | SQLTask is finishing | SQL作业成功结束 |
常见问题诊断
编译阶段
作业处于编译阶段的特征是有 logview,但还没有执行计划。根据 logview 的子状态(SubStatusHistory)可以进一步细分为调度、优化、生成物理执行计划、数据跨集群复制等子阶段。
编译阶段的问题主要表现为在某个子阶段卡住,即作业长时间停留在某一个子阶段。下面将介绍作业停留在每个子阶段的可能原因和解决方法。
调度阶段
【特征】子状态为“Waiting for cluster resource”,作业排队等待被编译。
【该阶段作业卡住的可能原因 1 】计算集群资源紧缺。
【解决方法】查看计算集群的状态,需要等待计算集群的资源。
【该阶段作业卡住的可能原因 2 】编译资源池资源不够:可能有人不小心用脚本一次提交太多作业,把编译资源池占满了。
优化阶段
【特征】子状态为“SQLTask is optimizing query”,优化器正在优化执行计划。
【该阶段作业卡住的可能原因】执行计划复杂,需要等待较长时间做优化。
生成物理执行计划阶段
【特征】子状态为“SQLTask is generating execution plan”。
【该阶段作业卡住的可能原因 1 】读取的分区太多。每个分区需要去根据分区信息来决定处理方式,决定 split,并且会写到生成的执行计划中。
【解决方法】需要好好设计 SQL,减少分区的数量,包括:分区裁剪、筛除不需要读的分区、把大作业拆成小作业。
【该阶段作业卡住的可能原因 2 】小文件太多。ODPS 会根据文件大小决定 split,小文件多了会导致计算 split 的过程耗时增加。
产生小文件的原因主要有两个:
- 我们使用 Tunnel 上传数据时操作不正确(例如每上传一条数据就重新 new 一个 upload session)
对分区表进行 insert into 操作的时候,会在 partition 目录下面生成一个新文件。
【解决方法】
- 使用 TunnelBufferedWriter 接口,可以更简单的进行上传功能,同时避免小文件。
- 执行一次
alter table merge smallfiles;
让 odps 把小文件 merge 起来。
【注意】上面提到的“太多”不是指几十、几百个。基本都是要上万,上十万才会对生成物理执行计划的时间产生较大影响。
数据跨集群复制阶段
【特征】子状态列表里面出现多次“Task rerun”,result 里有错误信息“FAILED: ODPS-0110141:Data version exception”。作业看似失败了,实际还在执行,说明作业正在做数据的跨集群复制。
【该阶段作业卡住的可能原因 1 】project 刚做集群迁移,往往前一两天有大量需要跨集群复制的作业。
【解决方法】这种情况是预期中的跨集群复制,需要用户等待。
【该阶段作业卡住的可能原因 2 】可能是作业提交错集群,或者是中间 project 做过迁移,分区过滤没做好,读取了一些比较老的分区。
【解决方法】
- 检查作业提交的集群是否正确。
用户可以通过 Logview1.0 点击 Status->System Info,或 Logview2.0 任务详情页左侧的 BasicInfo 查看作业提交的集群。
- 过滤掉不必要读取的老分区。
执行阶段
【特征】logview 的 detail 界面有执行计划(执行计划没有全都绿掉),且作业状态还是 Running。
执行阶段卡住或执行时间比预期长的主要原因有等待资源,数据倾斜,UDF 执行低效,数据膨胀等等,下面将具体介绍每种情况的特征和解决思路。
等待资源
【特征】instance 处于 Ready 状态,或部分 instance 是 Running,部分是 Ready 状态。需要注意的是,如果 instance 状态是 Ready 但有 debug 历史信息,那么可能是 instance fail 触发重试,而不是在等待资源。
【解决思路】
- 确定排队状态是否正常。
可以通过 logview 的排队信息“Queue”看作业在队列的位置;
或着通过 BCC 查看 project 当前的 quota 组作业信息、系统资源的的使用情况和历史曲线等等。
如果某项资源的使用率已经接近甚至超过配额了,那么证明 quota 组资源紧张,作业有排队是正常的。作业的调度顺序不仅与作业提交时间、优先级有关,还和作业所需内存或CPU资源大小能否被满足有关,因此合理设置作业的参数很重要
- 查看 quota 组中运行的作业。
可能会有人不小心提交了低优先级的大作业(或批量提交了很多小作业),占用了太多的资源,可以和作业的 owner 协商,让他先把作业 kill 掉,把资源让出来。
- 考虑去其他 quota 组的 project 跑。
只要有权限,是可以在别的 project 下访问另一个 project 的表的。当然,有可能会存在跨集群复制。
- 找 PE 扩容(从其他 quota 组调整资源过来)。普通用户请联系 project owner 或者 BU ODPS 接口人,由他们跟进调整资源或扩容需求。
资源组的配额一般都是经过多方考虑设置的,除非你的集群长期资源不足,否则一般不好申请。而且如果你的水位没到 MAX,又没有资源的话,很可能其他 quota 组也忙得不行。
数据倾斜
【特征】task 中大多数 instance 都已经结束了,但是有某几个 instance 却迟迟不结束(长尾)。如下图中大多数(358个)instance 都结束了,但是还有 18 个的状态是 Running,这些 instance 运行的慢,可能是因为处理的数据多。
【解决方法】需要找到造成数据倾斜的具体位置,对症下药。下面列出了定位长尾实例的常用方法:
- 利用 MaxCompute Studio 的作业执行图及作业详情功能来分析作业运行情况,定位到长尾实例,找到导致长尾的数据来源。
- 利用 Logveiw2.0 查看任务执行图和 instance 运行情况来定位长尾实例。
在确定造成数据倾斜的实例、数据来源等信息后,用户需要针对性的对代码甚至算法做一定的修改。
UDF执行低效
这里的 UDF 泛指各种用户自定义的扩展,包括UDF,UDAF,UDTF,UDJ,UDT等。
【特征】某个 task 执行效率低,且该 task 中有用户自定义的扩展。甚至是 UDF 的执行超时报错:“Fuxi job failed - WorkerRestart errCode:252,errMsg:kInstanceMonitorTimeout, usually caused by bad udf performance”。
【排查方法】任务报错时,可以在 MaxCompute Studio 中快速通过 DAG 图判断报错的 task 中是否包含 UDF。如下图,可以看到报错的task R4_3 包含用户使用 Java 语言编写的 UDF。双击 R4_3,展开 operator 视图,可以看到该 task 包含的所有 UDF 名称。
此外,在 task 的 stdout 日志里,UDF 框架会打印 UDF 输入的记录数、输出记录数、以及处理时间,如下图。通过这些数据可以看出 UDF 是否有性能问题。一般来讲,正常情况 Speed(records/s) 在百万或者十万级别,如果降到万级别,那么基本上就有性能问题了。
【解决思路】当有性能问题时,可以按照下面这些方法进行排查和优化:
- 检查 UDF 是否有 bug。
- 检查 UDF 函数是否与内置函数同名
- 使用内置函数代替 UDF。
- 将 UDF 函数进行功能拆分,部分用内置函数替换,内置函数无法实现的再用 UDF
- 优化 UDF 的 evaluate 方法。
- 预估 UDF 的执行时间。
- 调整内存参数。
数据膨胀
【特征】task 的输出数据量比输入数据量大很多。
比如 1G 的数据经过处理,变成了 1T,在一个 instance 下处理 1T 的数据,运行效率肯定会大大降低。输入输出数据量体现在 Task 的 I/O Record 和 I/O Bytes 这两项:
【解决思路】
检查代码是否有 bug
- JOIN 条件是不是写错了,变成笛卡尔积了;
- UDTF 是不是有问题,输出了太多数据。
检查 Aggregation 引起的数据膨胀
因为大多数 aggregator 是 recursive 的,中间结果先做了一层 merge,中间结果不大,而且大多数 aggregator 的计算复杂度比较低,即使数据量不小,也能较快完成。所以通常情况下这些操作问题不大,如:
- select 中使用 aggregation 按照不同维度做 distinct,每一次 distinct 都会使数据做一次膨胀;
- 使用 Grouping Sets (CUBE and ROLLUP) ,中间数据可能会扩展很多倍。
但是,有些操作如 collect_list、median 操作需要把全量中间数据都保留下来,可能会产生问题。
避免join引起的数据膨胀
比如:两个表 join,左表是人口数据,数据量很大,但是由于并行度足够,效率可观。右表是个维表,记录每种性别对应的一些信息(比如每种性别可能的坏毛病),虽然只有两种性别,但是每种都包含数百行。那么如果直接按照性别来 join,可能会让左表膨胀数百倍。要解决这个问题,可以考虑先将右表的行做聚合,变成两行数据,这样 join 的结果就不会膨胀了。