在切入正题之前首先介绍下阿里云扩展的mapreduce。
传统的MapReduce模型要求每一轮MapReduce操作之后,数据必须落地到分布式文件系统上(比如HDFS或
ODPS表)。 而一般的MapReduce应用通常由多个MapReduce作业组成,每个作业结束之后需要写入磁盘
,接下去的Map任务很多情况下只是读一遍数据, 为后续的Shuffle阶段做准备,这样其实造成了冗余的IO操
作。
ODPS的计算调度逻辑可以支持更复杂编程模型, 针对上面的那种情况,可以在Reduce后面直接执行下一次的
Reduce操作,而不需要中间插入一个Map操作。 基于此,ODPS提供了扩展的MapReduce模型,即可以支持
Map后连接任意多个Reduce操作,比如Map-Reduce-Reduce。
Hadoop Chain Mappper/Reducer也支持类似的串行化Map或Reduce操作,但和ODPS的扩展
MapReduce(MR2)模型有本质的区别, 因为Chain Mapper/Reducer还是基于传统的MapReduce模型,只是
可以在原有的Mapper或Reducer后面在增加一个或多个Mapper操作(不允许增加Reducer)。 这带来的好处是
用户可以复用之前的Mapper业务逻辑,可以把一个Map或Reduce拆成多个Mapper阶段,但本质上并没有改
变底层的调度和IO模型。
在实际应用中我遇到了一个比较棘手的问题,一开始觉得通过最简单的map和reduce两步操作在逻辑上就不太可行,后面使用pipeline模式完美的解决了该问题,话不多说直接上码!
package xxx.xxx import java.io.IOException import java.text.SimpleDateFormat import com.aliyun.odps.data.{Record, TableInfo} import com.aliyun.odps.mapred._ import com.aliyun.odps.pipeline.Pipeline import com.aliyun.odps.Column import com.aliyun.odps.OdpsType import scala.collection.mutable.ArrayBuffer /** * MapReduce Pipeline扩展<br/> * <p> * 在传统的MapReduce计算模型上做了扩展,可以在一轮Map/Reduce运算之后再加上一个或多个Reduce, * 即Map-Reduce-Reduce-Reduce...。一个Pipeline由多个节点顺序组成,每个节点是Mapper或Reducer<br/><br/> * 和普通的MapReduce一样,使用{@link Job}定义并提交作业,包括输入输入表的设置等。 * 除此之外,还需要定义一个Pipeline对象(使用Pipeline.builder()方法),添加一个Mapper以及一个 * 或多个Reducer。 * <br/><br/> * 任何一个Mapper或处于中间状态的Reducer都需要显式定义输出结果的Key和Value的Schema定义 * (定义方式类似于{@link JobConf#setMapOutputKeySchema(Column[])})。同时还可以设置 * OutputKeySortColumns、PartitionColumns等。 */ class LGTZMapper extends MapperBase { private var KEY: Record = _ private var VALUE: Record = _ val sdf = new SimpleDateFormat("yyyyMMddHHmmss") @throws[IOException] override def setup(context: Mapper.TaskContext) { KEY = context.createMapOutputKeyRecord VALUE = context.createMapOutputValueRecord } @throws[IOException] override def map(recordNum: Long, record: Record, context: Mapper.TaskContext) { def validateFunc(ValueArray: Array[String]): Boolean = { var isValid = false //0:xb_dm, 1:gmsfhm, 2:rzsj, |