Combiner和partition的作用
1.conbiner就相当于是本地reduce,输入的数据类型和输出的数据类型一致,
在逻辑上不能影响最终的结果输出,例如求和,最大值不会影响,但是求平均值就有影响了
combiner存在的意义在于当网络io成为瓶颈的时候可以使用combiner,此时会减少map与reduce之间数据量的传输,他的原理在于在map端把同一个key的键值对合并在一起,计算规则与reduce一样,所以combine又称为本地reduce
在哪里使用combiner
map输出数据根据分区排序完成之后,在写入文件之前进行一次combine操作(前提是作业中设置了这个操作)
如果map输出比较大,溢出文件大于3(此值可以通过属性min.num.spills.for.combine配置),在merge的过程(多个spill文件合并为一个大文件)中前还会执行combiner操作;
partition的作用
在将map()函数处理后得到的(key,value)对写入到缓冲区之前,需要先进行分区操作,这样就能把map任务处理的结果发送给指定的reducer去执行,从而达到负载均衡,避免数据倾斜。MapReduce提供默认的分区类(HashPartitioner),其核心代码如下
public class HashPartitioner<K, V> extends Partitioner<K, V> {
/** Use {@link Object#hashCode()} to partition. */
public int getPartition(K key, V value,
int numReduceTasks) {
return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
}
}
getPartition()方法有三个参数,前两个指的是mapper任务输出的键值对,而第三个参数指的是设置的reduce任务的数量,默认值为1。因为任何整数与1相除的余数肯定是0。也就是说默认的getPartition()方法的返回值总是0,也就是Mapper任务的输出默认总是送给同一个Reducer任务,最终只能输出到一个文件中。如果想要让mapper输出的结果给多个reducer处理,那么只需要写一个类,让其继承Partitioner类,并重写getPartition()方法,让其针对不同情况返回不同数值即可。并在最后通过job设置指定分区类和reducer任务数量即可。
mapreduce:实现join的几种方法
reduce side join
reduce side join是一种最简单的join方式,其主要思想如下:
在map阶段,map函数同时读取两个文件File1和File2,为了区分两种来源的key/value数据对,对每条数据打一个标签> (tag),比如:tag=0表示来自文件File1,tag=2表示来自文件File2。即:map阶段的主要任务是对不同文件中的数据打标签。> 在reduce阶段,reduce函数获取key相同的来自File1和File2文件的value list, 然后对于同一个key,对File1和File2中的数据进行join(笛卡尔乘积)。即:reduce阶段进行实际的连接操作。
map side join
之所以存在reduce side join,是因为在map阶段不能获取所有需要的join字段,即:同一个key对应的字段可能位于不同map中。Reduce side join是非常低效的,因为shuffle阶段要进行大量的数据传输。 Map side join是针对以下场景进行的优化:两个待连接表中,有一个表非常大,而另一个表非常小,以至于小表可以直接存放到内存中。这样,我们可以将小表复制多 份,让每个map task内存中存在一份(比如存放到hash table中),然后只扫描大表:对于大表中的每一条记录key/value,在hash table中查找是否有相同的key的记录,如果有,则连接后输出即可,在对多个表join连接操作时,将小表放在join的左边,大表放在Jion的右边,在执行这样的join连接时小表中的数据会被缓存到内存当中,这样可以有效减少发生内存溢出错误的几率
设置参数
hive.map.aggr = true
hive.groupby.skewindata=true 还有其他参数
SQL语言调节
比如: group by维度过小时:采用sum() group by的方式来替换count(distinct)完成计算
StreamTable
在多表级联时,一般大表都写在最后,因为写在最后的表默认使用stream形式加载,其他表的结果缓存在内存中。
可以使用/*+ STREAMTABLE(a) */ 来标示具体哪个表使用stream形式。在表关联时,使用该标识来指出大表,能避免数据表过大导致占用内存过多而产生的问题。
```SELECT /*+ STREAMTABLE(a) */ a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)``
SemiJoin
SemiJoin,也叫半连接,是从分布式数据库中借鉴过来的方法。它的产生动机是:对于reduce side join,跨机器的数据传输量非常大,这成了join操作的一个瓶颈,如果能够在map端过滤掉不会参加join操作的数据,则可以大大节省网络IO。
实现方法很简单:选取一个小表,假设是File1,将其参与join的key抽取出来,保存到文件File3中,File3文件一般很小,可以放到
内存中。在map阶段,使用DistributedCache将File3复制到各个TaskTracker上,然后将File2中不在File3中的 key对应的记录过滤掉,剩下的reduce阶段的工作与reduce side join相同。