阿里云数加-大数据计算服务MaxCompute产品地址:https://www.aliyun.com/product/odps
A: 对于ClassNotFoundException异常,一般是依赖的class不在你的jar包中,需要把依赖的库打到作业jar包中或者单独上传,并在-resources中指定;
对于NoClassDefFoundError异常,先看看依赖class是否存在于你的jar包,很多情况下是由于版本冲突导致的,可能你依赖的库和服务端自带的jar有冲突。
A: 在MaxCompute中类似MR这类分布式数据处理框架,用户的代码一般在以下两个地点执行:
A:如果没有输入表是可以直接指定map数目setNumMapTasks
有输入表的话,setNumMapTasks不生效,需要通过setSplitSize
来控制map数,默认是256M。
A: 通过JobConf的接口setNumReduceTasks
可以设置。
对于pipeline作业,Jobconf的接口同样可以设置,只不过设置后所有reduce阶段的个数都是同样的值。
如果要分阶段设置,设置方式如下:
Pipeline pipeline = Pipeline.builder()
.addMapper(TokenizerMapper.class)
...
.addReducer(SumReducer.class).setNumTasks(5)
...
.addReducer(IdentityReducer.class).setNumTasks(1)
.createPipeline();
A:mapper或reducer的内存由两部分组成,JVM的heap memory和JVM 之外的框架相关内存。
设置JVM内存的接口是(都是Java逻辑的话,调节内存是用下面两个接口):
setMemoryForMapperJVM
和setMemoryForReducerJVM
(默认是1024 单位MB)
设置框架内存(c++部分的)的接口是(一般不需要设置):
setMemoryForMapTask
和setMemoryForReduceTask
(默认是2048 单位MB)
A: 会覆盖输出表或分区之前的内容
A: 通常情况下,GroupingColumns包含在KeySortColumns中,KeySortColumns和PartitionColumns要包含在Key schema中。
A: 抛异常就可以,例如throw new RuntimeException("XXX");
会导致job失败,job也就结束了。
A:因为有backup instance在跑,产生backup instance一般是因为有某些map instances明显慢于其他的,就会在别的机器上启动一个同样的worker来跑,这个功能类似于hadoop的预测执行,只要其中某个成功跑完,其他的就可以停掉了(变为interrupted)
A: 参考:http://repo.aliyun.com/java-sdk-doc/com/aliyun/odps/mapred/Mapper.TaskContext.html
使用Mapper.TaskContext的接口getInputTableInfo()
,会得到输入表的TableInfo
对象
每个map worker只会处理来自单一表或分区的数据,在mapper的setup阶段获取该信息即可。
A: 参考如下代码:
import com.aliyun.odps.mapred.Partitioner;
...
public static class MyPartitioner extends Partitioner {
@Override
public int getPartition(Record key, Record value, int numPartitions) {
// numPartitions即对应reducer的个数
// 通过该函数决定map输出的key value去往哪个reducer
String k = key.get(0).toString();
return k.length() % numPartitions;
}
}
在jobconf里进行设置:jobconf.setPartitionerClass(MyPartitioner.class)
另外需要在jobconf里明确指定reducer的个数:jobconf.setNumReduceTasks(num)
A: 类似如下:
//key按这些列排序job.setOutputKeySortColumns(new String[] { "custid", "msgtype","amount" });
//设置每个列正序还是倒序job.setOutputKeySortOrder(new SortOrder[]{SortOrder.ASC,SortOrder.ASC,SortOrder.DESC});
A: 报这个错的原因是mapper或者reducer有逻辑执行时间特别长,且没有从输入表的读数据或者写出数据,超过默认10min后,会报这个异常;有两种解决方法:
odps.function.timeout
或者设置JobConf#setFunctionTimeout
,最长可以设置为3600,即一个小时。TaskContext#progress()
,注意progress不要调用过于频繁,否则有性能问题,能确保两次调用之间的时间小于设置的timeout时间即可。A:是的,为了减少对象的开销,框架对于map, reduce接口里的Record对象是复用的,也就是说每次map或者reduce的每次迭代,Record对象没有变,只是里面的数据变化了。如果要保存上一次的Record需要toArray()
拿到里面的数据对象进行保存。具体可以参考:http://repo.aliyun.com/java-sdk-doc/com/aliyun/odps/mapred/Reducer.html
A:如果写的Record对象是复用的,如果某个值没有新set,则还是保留着之前的值对象。目前没有直接可以清空的api可以用,可以通过Record.getColumnCount拿到column count,用一个for 循环去一一set null即可。
A:参考:多路输入输出示例
对于多路输入,每个输入源对应单独的一个Map阶段,即一个map task只会读取一个输入表的数据。可以指定一个表的多级分区列来作为一个输入,例如a, b, c三分区列,指定分区时可以指定a=1/b=1/c=2类似这样。
如果同一级别的多个分区,则需要各自作为单独的分区输入,例如一个表的a=1和a=3分区作为多路输入的俩不同的输入,需要分别指定。
maponly的作业也同样支持多路输入输出,实现方法类似。
A: 可以使用如下的方式拿到logview的url
RunningJob rj = JobClient.runJob(job);
com.aliyun.odps.Instance instance = SessionState.get().getOdps().instances().get(rj.getInstanceID());
String logview = SessionState.get().getOdps().logview().generateLogView(instance, 7 * 24);
System.out.println(logview);
A: 可以按如下的方式指定:
InputUtils.addTable(TableInfo.builder().projectName("test_project_name").tableName("test_table_name").build(), job);
通过TableInfo.builder()
的projectName
接口来指定,如果不指定,默认值是在运行MR作业的那个project.
A: 有些业务场景需要区分不同的Mapper或Reducer,可以通过TaskContext
的getTaskID
接口获取到一个Mapper/Reducer独有的id。
String id = context.getTaskID().toString();
A:首先project要开通jni的相关权限,在编译准备好so文件后,需要将so以file类型的形式添加为Resource,并在MR作业提交的时候-resources参数里指定,例如:
add file libtestjni.so as libtestjni.so -f;
jar -resources testmr.jar,libtestjni.so -classpath testmr.jar Test.MRDriver xxx xxx;
在MR的java代码使用jni的时候要注意,使用方式如下:
System.loadLibrary("testjni"); // 这里不要写成libtestjni.so,否则会报错,原因是java会自动添加lib前缀和.so后缀的
jni的使用方法可以参考:https://www.ibm.com/developerworks/cn/java/l-linux-jni/
A: MaxCompute上的资源(file, table, archive等)可以类比于Hadoop的DistributedCache来理解,同样是会分发到每个计算节点上去,worker再从本地来读取,因而资源文件不能过大,否则分发资源就是一个瓶颈,目前默认有2G的总资源大小限制。
读取资源表,Archive资源总体上来说和读取file类型资源是类似的,只是使用的接口不同。读取资源文件的方法可以参考文档:使用资源示例
对于表资源:
将表添加为资源表: add table xxx as xxx -f;
读资源表的接口为:TaskContext#readResourceTable
对于Archive资源:
将本地archive(.tar, .zip等archive文件)上传为资源: add archive as xxx -f;
读archive资源的接口为:TaskContext#readResourceArchiveAsStream
...不断更新中...