MapReduce实现基本SQL操作的原理

MapReduce实现基本SQL操作的原理:

Join的实现原理:

select  s.sname,  c.cname  from  class c  join  student s on c.uid = s.uid;

在map的输出value中为不同表的数据打上tag标记,在reduce阶段根据tag判断数据来源。MapReduce的过程如下:
在这里插入图片描述
Common Join:

适用场景:适用于所有类型的表关联与其他类型join不支持的join类型,比如:full outer join

Map阶段:读取源表的数据,Map输出时候以Join on条件中的列为key,如果Join有多个关联键,则以这些关联键的组合作为key;Map输出的value为join之后所关心的(select或者where中需要用到的)列,同时在value中还会包含表的Tag信息,用于标明此value对应哪个表。
Shuffle阶段:根据key的值进行hash,并将key/value按照hash值推送至不同的reduce中,这样确保两个表中相同的key位于同一个reduce中。
Reduce阶段:根据key的值完成join操作,期间通过Tag来识别不同表中的数据。


Map Join:

适用场景:小表(维度表)join大表(事实表),不适用与Right/Full outer join.

如果关联的表足够小,那么可以将小表加载到mapper的内存中,在map端完成join,减少shuffle和reduce阶段。MapReduce Local Task会在真正的MapReduce Join Task之前,从HDFS读取小表,然后将其转成一个tar文件,最后将文件上传至HDFS Cache.MapReduce Local Task运行过程中,可能由于内存不足而失败,可以通过设置hive.mapjoin.localtask.max.memory.usage来改变Local Task可使用的内存大小。

Group By的实现原理:

select uid,city, count(*) from student group by uid,city;

MapReduce因为要从多个文件块中拉取表数据,因此会有shuffle sort这一步
在这里插入图片描述
Map端聚合: Map端进行预聚合,减少shuffle数据量,类似于MR中的Combiner。默认情况下,Hive 会尽可能地使用 Map 端Aggregation,但是如果 Hash Map不能有效地降低内存使用,那么会降级到普通的Aggregation,即 Map 端仅做Shuffle Write,Reducer执行真正的聚合运算。
倾斜: 生成的查询计划有两个 MapReduce 任务。在第一个 MapReduce 中,map 的输出结果集合会随机分布到 reduce 中, 每个 reduce 做部分聚合操作,并输出结果。这样处理的结果是,相同的 Group By Key 有可 能分发到不同的 reduce 中,从而达到负载均衡的目的;第二个 MapReduce 任务再根据预处 理的数据结果按照 Group By Key 分布到 reduce 中(这个过程可以保证相同的 Group By Key 分布到同一个 reduce 中),最后完成最终的聚合操作。

Distinct的实现原理:

select city, count(distinct uid) from student group by city;

在这里插入图片描述

  1. 第一步先在mapper计算部分值,会以city和uid作为key,如果是distinct并且之前已经出现过,则忽略这条计算。第一步是以组合为key,第二步是以city为key.
  2. ReduceSink是在mapper.close()时才执行的,在GroupByOperator.close()时,把结果输出。注意这里虽然key是city和uid,但是在reduce时 分区是按city来的.
  3. 第一步的distinct计算的值没用,要留到reduce计算的才准确。这里只是减少了key组合相同的行。
  4. distinct通过比较lastInvoke判断要不要+1(因为在reduce是排序过了的,所以判断distict的字段变了没有,如果没变,则不+1)

Explain执行计划:


hive> explain select city, count(distinct uid) from student group by city;
OK
ABSTRACT SYNTAX TREE:
  (TOK_QUERY (TOK_FROM (TOK_TABREF (TOK_TABNAME student))) (TOK_INSERT (TOK_DESTINATION (TOK_DIR TOK_TMP_FILE)) (TOK_SELECT (TOK_SELEXPR (TOK_TABLE_OR_COL city)) (TOK_SELEXPR (TOK_FUNCTIONDI city (TOK_TABLE_OR_COL uid)))) (TOK_GROUPBY (TOK_TABLE_OR_COL city))))
 
STAGE DEPENDENCIES://显示各个阶段的依赖关系
  Stage-1 is a root stage//阶段一
  Stage-0 is a root stage//阶段二
 
STAGE PLANS:
  Stage: Stage-1
    Map Reduce
      Alias -> Map Operator Tree://Map阶段
        student 
          TableScan   //TableScan以student表作为输入
            alias: student
            Select Operator//列裁剪,取出uid,city字段
              expressions:
                    expr: city
                    type: string
                    expr: uid
                    type: string
              outputColumnNames: city, uid
              Group By Operator //map聚集
                aggregations:
                      expr: city(DISTINCT uid) //group by Operator 的聚集表达式
                bucketGroup: false
                keys:
                      expr: city
                      type: string
                      expr: uid
                      type: string
                mode: hash //hash方式
                outputColumnNames: _col0, _col1, _col2	//为临时结果字段按规则起的临时字段名
                Reduce Output Operator
                  key expressions: //输出的键
                        expr: _col0 //city
                        type: string
                        expr: _col1 //uid
                        type: string
                  sort order: ++
                  Map-reduce partition columns: //这里是按group by的字段分区的
                        expr: _col0 //这里表示city
                        type: string
                  tag: -1
                  value expressions:
                        expr: _col2
                        type: bigint
      Reduce Operator Tree:
        Group By Operator //第二次聚集
          aggregations:
                expr: city(DISTINCT KEY._col1:0._col0) //uid:city
          bucketGroup: false
          keys:
                expr: KEY._col0 //city
                type: string
          mode: mergepartial //合并
          outputColumnNames: _col0, _col1
          Select Operator //列裁剪
            expressions:
                  expr: _col0
                  type: string
                  expr: _col1
                  type: bigint
            outputColumnNames: _col0, _col1
            File Output Operator //输出结果到文件
              compressed: false
              GlobalTableId: 0
              table:
                  input format: org.apache.hadoop.mapred.TextInputFormat
                  output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
 
  Stage: Stage-0
    Fetch Operator
      limit: -1

相关参数

# 是否开启mapper端聚合
hive.map.aggr
# 是否开启,如果数据倾斜,是否优化group by为两个MR job
#该配置会触发hive增加额外的mr过程,随机化key后进行聚合操作得到中间结果,再对中间结果执行最终的聚合操作。
#count(distinct)操作比较特殊,无法进行中间的聚合操作,因此该参数对有count(distinct)操作的sql不适用。
hive.groupby.skewindata
# 用于map端聚合的hashtable最大可用内存,如果超过该内存比例,将flush到磁盘
hive.map.aggr.hash.force.flush.memory.threshold

# 可以用于mapper端hatable的内存比例
hive.map.aggr.hash.percentmemory (Default: 0.5) – Percent of total map task memory that can be used for hash table.
# 如果hashtable大小/输入行数 大于该阈值,那么停止hash聚合,转为sort-based aggregation
hive.map.aggr.hash.min.reduction (Default: 0.5)
# 每隔多少行,检测hashtable大小和input row比例是否超过阈值
hive.groupby.mapaggr.checkinterval 

# 是否开启bucket group by
hive.optimize.groupby


# 是否自动转换common join为map join
set hive.auto.convert.join=true;
# 如果join的小表和小于该阈值,会尝试将Common join 转换成map join。通过explain命令,可以发现Operator树中有conditional Operator。 如果n-1张表大小和,小于该阈值,则生成conditional tasks。
hive.smalltable.filesize or hive.mapjoin.smalltable.filesize
# 如果join的小表小于该阈值,会直接将Common join转换成Map join。需要考虑到数据解压之后的实际大小,hive表在被解压后,文件大小可能会增大10倍。
hive.auto.convert.join.noconditionaltask.size

参考:
Hivesql的编译过程

猜你喜欢

转载自blog.csdn.net/Pioo_/article/details/108240953