作为数据分析中经常进行的join 操作,传统DBMS 数据库已经将各种算法优化到了极致,而对于hadoop 使用的mapreduce 所进行的join 操作,去年开始也是有各种不同的算法论文出现,讨论各种算法的适用场景和取舍条件,本文讨论hive 中出现的几种join 优化,然后讨论其他算法实现,希望能给使用hadoop 做数据分析的开发人员提供一点帮助.

Facebook 今年在yahoo 的hadoop summit 大会上做了一个关于最近两个版本的hive 上所做的一些join 的优化,其中主要涉及到hive 的几个关键特性: 值分区 , hash 分区 , map join , index ,

1、Common Join

最为普通的join策略,不受数据量的大小影响,也可以叫做reduce side join ,最没效率的一种join 方式. 它由一个mapreduce job 完成.

首先将大表和小表分别进行map 操作, 在map shuffle 的阶段每一个map output key 变成了table_name_tag_prefix + join_column_value , 但是在进行partition 的时候它仍然只使用join_column_value 进行hash.

每一个reduce 接受所有的map 传过来的split , 在reducce 的shuffle 阶段,它将map output key 前面的table_name_tag_prefix 给舍弃掉进行比较. 因为reduce 的个数可以由小表的大小进行决定,所以对于每一个节点的reduce 一定可以将小表的split 放入内存变成hashtable. 然后将大表的每一条记录进行一条一条的比较.

2、Map Join

Map Join 的计算步骤分两步,将小表的数据变成hashtable广播到所有的map 端,将大表的数据进行合理的切分,然后在map 阶段的时候用大表的数据一行一行的去探测(probe) 小表的hashtable. 如果join key 相等,就写入HDFS.

map join 之所以叫做map join 是因为它所有的工作都在map 端进行计算.

hive 在map join 上做了几个优化:

  • hive 0.6 的时候默认认为写在select 后面的是大表,前面的是小表, 或者使用 /*+mapjoin(map_table) */ 提示进行设定. hive 0.7 的时候这个计算是自动化的,它首先会自动判断哪个是小表,哪个是大表,这个参数由(hive.auto.convert.join=true)来控制. 然后控制小表的大小由(hive.smalltable.filesize=25000000L)参数控制(默认是25M),当小表超过这个大小,hive 会默认转化成common join. 你可以查看HIVE-1642.

  • 首先小表的Map 阶段它会将自己转化成MapReduce Local Task ,然后从HDFS 取小表的所有数据,将自己转化成Hashtable file 并压缩打包放入DistributedCache 里面.

    目前hive 的map join 有几个限制,一个是它打算用BloomFilter 来实现hashtable , BloomFilter 大概比hashtable 省8-10倍的内存, 但是BloomFilter 的大小比较难控制.

    现在DistributedCache 里面hashtable默认的复制是3份,对于一个有1000个map 的大表来说,这个数字太小,大多数map 操作都等着DistributedCache 复制.

3、Bucket Map Join

hive 建表的时候支持hash 分区通过指定clustered by (col_name,xxx ) into number_buckets buckets 关键字.

当连接的两个表的join key 就是bucket column 的时候,就可以通过

hive.optimize.bucketmapjoin= true

来控制hive 执行bucket map join 了, 需要注意的是你的小表的number_buckets 必须是大表的倍数. 无论多少个表进行连接这个条件都必须满足.(其实如果都按照2的指数倍来分bucket, 大表也可以是小表的倍数,不过这中间需要多计算一次,对int 有效,long 和string 不清楚)

Bucket Map Join 执行计划分两步,第一步先将小表做map 操作变成hashtable 然后广播到所有大表的map端,大表的map端接受了number_buckets 个小表的hashtable并不需要合成一个大的hashtable,直接可以进行map 操作,map 操作会产生number_buckets 个split,每个split 的标记跟小表的hashtable 标记是一样的, 在执行projection 操作的时候,只需要将小表的一个hashtable 放入内存即可,然后将大表的对应的split 拿出来进行判断,所以其内存限制为小表中最大的那个hashtable 的大小.

Bucket Map Join 同时也是Map Side Join 的一种实现,所有计算都在Map 端完成,没有Reduce 的都被叫做Map Side Join ,Bucket 只是hive 的一种hash partition 的实现,另外一种当然是值分区.

create table a  (xxx) partition by (col_name)

不过一般hive 中两个表不一定会有同一个partition key, 即使有也不一定会是join key. 所以hive 没有这种基于值的map side join, hive 中的list partition 主要是用来过滤数据的而不是分区. 两个主要参数为(hive.optimize.cp = true 和 hive.optimize.pruner=true)

hadoop 源代码中默认提供map side join 的实现, 你可以在hadoop 源码的src/contrib/data_join/src 目录下找到相关的几个类.  其中TaggedMapOutput 即可以用来实现hash 也可以实现list , 看你自己决定怎么分区. Hadoop Definitive Guide 第8章关于map side join 和side data distribution 章节也有一个例子示例怎样实现值分区的map side join.

4、Sort Merge Bucket Map Join

Bucket Map Join 并没有解决map join 在小表必须完全装载进内存的限制, 如果想要在一个reduce 节点的大表和小表都不用装载进内存,必须使两个表都在join key 上有序才行,你可以在建表的时候就指定sorted by join key 或者使用index 的方式.

set hive.optimize.bucketmapjoin = true;

set hive.optimize.bucketmapjoin.sortedmerge = true;

set hive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;

Bucket columns == Join columns == sort columns

这样小表的数据可以每次只读取一部分,然后还是用大表一行一行的去匹配,这样的join 没有限制内存的大小. 并且也可以执行全外连接.

例子参考:http://superlxw1234.iteye.com/blog/1545150

5、Skew Join

真实数据中数据倾斜是一定的, hadoop 中默认是使用

hive.exec.reducers.bytes.per.reducer = 1000000000

也就是每个节点的reduce 默认是处理1G大小的数据,如果你的join 操作也产生了数据倾斜,那么你可以在hive 中设定

set hive.optimize.skewjoin = true; 
set hive.skewjoin.key = skew_key_threshold (default = 100000)

hive 在运行的时候没有办法判断哪个key 会产生多大的倾斜,所以使用这个参数控制倾斜的阈值,如果超过这个值,新的值会发送给那些还没有达到的reduce, 一般可以设置成你

(处理的总记录数/reduce个数)的2-4倍都可以接受.

倾斜是经常会存在的,一般select 的层数超过2层,翻译成执行计划多于3个以上的mapreduce job 都很容易产生倾斜,建议每次运行比较复杂的sql 之前都可以设一下这个参数. 如果你不知道设置多少,可以就按官方默认的1个reduce 只处理1G 的算法,那么  skew_key_threshold  = 1G/平均行长. 或者默认直接设成250000000 (差不多算平均行长4个字节)

6、Left Semi Join

hive 中没有in/exist 这样的子句,所以需要将这种类型的子句转成left semi join. left semi join 是只传递表的join key给map 阶段 , 如果key 足够小还是执行map join, 如果不是则还是common join.

join 策略中的难点

大多数只适合等值连接(equal join) ,

范围比较和全外连接没有合适的支持

提前分区,零时分区,排序,多种不同执行计划很难评价最优方案.

没有考虑IO 比如临时表,网络消耗和网络延迟时间,CPU时间,

最优的方案不代表系统资源消耗最少.

转载:几种 hive join 类型简介的更多相关文章

  1. Hive JOIN的基本操作 及 内部实现

    1.HIVE基本操作: [一起学Hive]之十一-Hive中Join的类型和用法 注:HIve不支持非等值连接: 什么是等值连接: //Oracle SQL 不等值连接 //通过不等值连接查找7788 ...

  2. sleep、yield、join方法简介与用法 sleep与wait区别 多线程中篇(十五)

    Object中的wait.notify.notifyAll,可以用于线程间的通信,核心原理为借助于监视器的入口集与等待集逻辑 通过这三个方法完成线程在指定锁(监视器)上的等待与唤醒,这三个方法是以锁( ...

  3. Hive 表类型简述

    Hive 表类型简述   表类型一.管理表或内部表Table Type:  MANAGED_TABLE example: create table  Inner(id int,name string, ...

  4. Hive Join

    最近被朋友问到有关于Hive Join的问题,保守回答过后,来补充补充知识: Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能. 一.Hi ...

  5. 【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

    [嵌入式开发]ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )     一. 内存 ...

  6. Hive Join优化

    在阐述Hive Join具体的优化方法之前,首先看一下Hive Join的几个重要特点,在实际使用时也可以利用下列特点做相应优化: 1. 只支持等值连接 2. 底层会将写的HQL语句转换为MapRed ...

  7. Hive——join的使用

    Hive--join的使用 hive中常用的join有:inner join.left join .right join .full join.left semi join.cross join.mu ...

  8. 深入理解DOM节点类型第一篇——12种DOM节点类型概述

    × 目录 [1]元素 [2]特性 [3]文本[4]CDATA[5]实体引用[6]实体名称[7]处理指令[8]注释[9]文档[10]文档类型[11]文档片段[12]DTD 前面的话 DOM是javasc ...

  9. iOS中常用的四种数据持久化方法简介

    iOS中常用的四种数据持久化方法简介 iOS中的数据持久化方式,基本上有以下四种:属性列表.对象归档.SQLite3和Core Data 1.属性列表涉及到的主要类:NSUserDefaults,一般 ...

随机推荐

  1. iptables综述

    1 概述 如下图所示,iptables共有Filter,Nat,Mangle和RAW共四个table,每个table还有若干个chain,每个chain中还包含若干个rule 1.1 Filter t ...

  2. 最长括号化长度 java

    1:求最长括号, ()(()()( 例如,它的最长符合括号化的长度为4 package com.li.huawei; import java.util.Arrays; import java.util ...

  3. controller中两个方法之间共享一个变量LinkedHashMap

    1:引用传递,创建一个变量,给两个线程都传递进去. 2:静态修饰 static  通过该修饰符说明,该变量只有一份,  所有线程共用一份. 例如下面的htmlidMap通过static变量修饰, up ...

  4. java.lang.UnsatisfiedLinkError: org.apache.hadoop.util.NativeCrc32.nativeComputeChunkedSumsByteArray(II[BI[BIILjava/lang/String;JZ)V

    环境: Spark2.1.0 .Hadoop-2.7.5   代码运行系统:Win 7在运行Spark程序写出文件(savaAsTextFile)的时候,我遇到了这个错误: // :: ERROR U ...

  5. WebDriver API 实例详解(四)

    三十一.使用页面的文字内容识别和处理新弹出的浏览器窗口 被测试网页的HTML源码: <html> <head> <meta charset="UTF-8&quo ...

  6. mariadb10.1.13GTID实现主从复制

    ---恢复内容开始--- 环境:centos6.5       mariadb:10.1.13-MariaDB GTID:GTID是有服务器的UUID和事务序号组成的唯一事务序号 ---UUID:N ...

  7. 3.1 Templates -- Handlerbars Basics(Handlerbars基础知识)

    一.简介 Ember.js使用Handlerbars模板库来强化应用程序的用户界面.它就像普通的HTML,但也给你嵌入表达式去改变现实的内容. Ember使用Handlerbars并且用许多新特性去扩 ...

  8. 5makefile

    makefile编译多个可执行文件1: 多个 C 文件编译成不同的目标文件2: 多个 C 文件编译成 一个目标文件 注意:makefile的文件名的三种形式(优先级排序)makefile>Mak ...

  9. echarts 不同区域背景色不同 废了我一天的时间

    var result = echarts.init(document.getElementById('result')); var option1 = { title: { text: '设备阶段故障 ...

  10. android,结合Timer和TimerTask实现定时任务

    当我们需要每隔一段时间执行一个任务的时候,就需要使用TimerTask了,下面是入门的例子, 值得注意的是Timer.TimerTask,cancel之后就需要重新声明一个对象,否则会报错的哦~ pa ...