Hive中小表与大表关联(join)的性能分析【转】
Hive中小表与大表关联(join)的性能分析
【转自:http://blog.sina.com.cn/s/blog_6ff05a2c01016j7n.html】
经常看到一些Hive优化的建议中说当小表与大表做关联时,把小表写在前面,这样可以使Hive的关联速度更快,提到的原因都是说因为小表可以先放到内存中,然后大表的每条记录再去内存中检测,最终完成关联查询。这样的原因看似合理,但是仔细推敲,又站不住脚跟。
多小的表算小表?如果所谓的小表在内存中放不下怎么办?我用2个只有几条记录的表做关联查询,这应该算是小表了,在查看reduce的执行日志时依然是有写磁盘的操作的。实际上reduce在接收全部map的输出后一定会有一个排序所有键值对并合并写入磁盘文件的操作。写入磁盘(spill)有可能是多次的,因此有可能会生成多个临时文件,但是最终都要合并成一个文件,即最终每一个reduce都只处理一个文件。
我做了一个实验,用1条记录的表和3亿多条记录的表做join,无论小表是放在join的前面还是join的后面,执行的时间几乎都是相同的。再去看reduce的执行日志,1条记录的表在join前或者join后两次查询的reduce日志几乎也是一摸一样的。如果按照上面的说法把join左侧的表放内存等待join右侧的表到内存中去检测,那么当3亿多条记录的表放在join左侧时,内存肯定是无法容下这么多记录的,势必要进行写磁盘的操作,那它的执行时间应该会比小表在join前时长很多才对,但事实并不是这样,也就说明了上面说到的原因并不合理。
事实上“把小表放在前面做关联可以提高效率”这种说法是错误的。正确的说法应该是“把重复关联键少的表放在join前面做关联可以提高join的效率。”
分析一下Hive对于两表关联在底层是如何实现的。因为不论多复杂的Hive查询,最终都要转化成mapreduce的JOB去执行,因此Hive对于关联的实现应该和mapreduce对于关联的实现类似。而mapreduce对于关联的实现,简单来说,是把关联键和标记是在join左边还是右边的标识位作为组合键(key),把一条记录以及标记是在join左边还是右边的标识位组合起来作为值(value)。在reduce的shuffle阶段,按照组合键的关联键进行主排序,当关联键相同时,再按照标识位进行辅助排序。而在分区段时,只用关联键中的关联键进行分区段,这样关联键相同的记录就会放在同一个value list中,同时保证了join左边的表的记录在value list的前面,而join右边的表的记录在value list的后面。
例如A join B ON (A.id = b.id) ,假设A表和B表都有1条id = 3的记录,那么A表这条记录的组合键是(3,0),B表这条记录的组合键是(3,1)。排序时可以保证A表的记录在B表的记录的前面。而在reduce做处理时,把id=3的放在同一个value list中,形成 key = 3,value list = [A表id=3的记录,B表id=3的记录]
接下来我们再来看当两个表做关联时reduce做了什么。Reduce会一起处理id相同的所有记录。我们把value list用数组来表示。
1) Reduce先读取第一条记录v[0],如果发现v[0]是B表的记录,那说明没有A表的记录,最终不会关联输出,因此不用再继续处理这个id了,读取v[0]用了1次读取操作。
2) 如果发现v[0]到v[length-1]全部是A表的记录,那说明没有B表的记录,同样最终不会关联输出,但是这里注意,已经对value做了length次的读取操作。
3) 例如A表id=3有1条记录,B表id=3有10条记录。首先读取v[0]发现是A表的记录,用了1次读取操作。然后再读取v[1]发现是B表的操作,这时v[0]和v[1]可以直接关联输出了,累计用了2次操作。这时候reduce已经知道从v[1]开始后面都是B 表的记录了,因此可以直接用v[0]依次和v[2],v[3]……v[10]做关联操作并输出,累计用了11次操作。
4) 换过来,假设A表id=3有10条记录,B表id=3有1条记录。首先读取v[0]发现是A表的记录,用了1次读取操作。然后再读取v[1]发现依然是A表的记录,累计用了2次读取操作。以此类推,读取v[9]时发现还是A表的记录,累计用了10次读取操作。然后读取最后1条记录v[10]发现是B表的记录,可以将v[0]和v[10]进行关联输出,累计用了11次操作。接下来可以直接把v[1]~v[9]分别与v[10]进行关联输出,累计用了20次操作。
5) 再复杂一点,假设A表id=3有2条记录,B表id=3有5条记录。首先读取v[0]发现是A表的记录,用了1次读取操作。然后再读取v[1]发现依然是A表的记录,累计用了2次读取操作。然后读取v[2]发现是B表的记录,此时v[0]和v[2]可以直接关联输出,累计用了3次操作。接下来v[0]可以依次和v[3]~v[6]进行关联输出,累计用了7次操作。接下来v[1]再依次和v[2]~v[6]进行关联输出,累计用了12次操作。
6) 把5的例子调过来,假设A表id=3有5条记录,B表id=3有2条记录。先读取v[0]发现是A表的记录,用了1次读取操作。然后再读取v[1]发现依然是A表的记录,累计用了2次读取操作。以此类推,读取到v[4]发现依然是A表的记录,累计用了5次读取操作。接下来读取v[5],发现是B表的记录,此时v[0]和v[5]可以直接关联输出,累计用了6次操作。然后v[0]和v[6]进行关联输出,累计用了7次操作。然后v[1]分别与v[5]、v[6]关联输出,累计用了9次操作。V[2] 分别与v[5]、v[6]关联输出,累计用了11次操作。以此类推,最后v[4] 分别与v[5]、v[6]关联输出,累计用了15次操作。
7) 额外提一下,当reduce检测A表的记录时,还要记录A表同一个key的记录的条数,当发现同一个key的记录个数超过hive.skewjoin.key的值(默认为1000000)时,会在reduce的日志中打印出该key,并标记为倾斜的关联键。
最终得出的结论是:写在关联左侧的表每有1条重复的关联键时底层就会多1次运算处理。
假设A表有一千万个id,平均每个id有3条重复值,那么把A表放在前面做关联就会多做三千万次的运算处理,这时候谁写在前谁写在后就看出性能的差别来了。
Hive中小表与大表关联(join)的性能分析【转】的更多相关文章
- 了解MySQL联表查询中的驱动表,优化查询,以小表驱动大表
一.为什么要用小表驱动大表 1.驱动表的定义 当进行多表连接查询时, [驱动表] 的定义为: 1)指定了联接条件时,满足查询条件的记录行数少的表为[驱动表] 2)未指定联接条件时,行数少的表为[驱动表 ...
- 3.mysql小表驱动大表的4种表连接算法
小表驱动大表 1.概念 驱动表的概念是指多表关联查询时,第一个被处理的表,使用此表的记录去关联其他表.驱动表的确定很关键,会直接影响多表连接的关联顺序,也决定了后续关联时的查询性能. 2.原则 驱动表 ...
- 小表驱动大表, 兼论exists和in
给出两个表,A和B,A和B表的数据量, 当A小于B时,用exists select * from A where exists (select * from B where A.id=B.id) ex ...
- 6.2 小表驱动大表(exists的应用)
1. 优化原则:小表驱动大表,即小数据集驱动大数据集. select * from A where id in (select id from B) 等价于: for select id from B ...
- Mysql优化原则_小表驱动大表IN和EXISTS的合理利用
//假设一个for循环 ; $i < ; $i++) { ; $i < ; $j++) { } } ; $i < ; $i++) { ; $i < ; $j++) { } } ...
- 查询优化--小表驱动大表(In,Exists区别)
Mysql 系列文章主页 =============== 本文将以真实例子来讲解小表驱动大表(In,Exists区别) 1 准备数据 1.1 创建表.函数.存储过程 参照 这篇(调用函数和存储过程批 ...
- MySQL高级知识(十六)——小表驱动大表
前言:本来小表驱动大表的知识应该在前面就讲解的,但是由于之前并没有学习数据批量插入,因此将其放在这里.在查询的优化中永远小表驱动大表. 1.为什么要小表驱动大表呢 类似循环嵌套 for(int i=5 ...
- MySql 小表驱动大表
在了解之前要先了解对应语法 in 与 exist. IN: select * from A where A.id in (select B.id from B) in后的括号的表达式结果要求之输出一列 ...
- [NewLife.XCode]扩展属性(替代多表关联Join提升性能)
NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netstandard,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode. 整个系列教程会大量结合示 ...
随机推荐
- 编程实践笔记{Java 线程 并发处理 Webservice}(转)
http://www.cnblogs.com/mingzi/archive/2009/03/09/1406694.html 1, 保证线程安全的三种方法: a, 不要跨线程访问共享变量 b, 使共享变 ...
- MATLAB 的循环语句
1.MATLAB while循环语法 在MATLAB 中 while循环的语法如下: while <expression> <statements> end while 循环反 ...
- 【LeetCode】215. Kth Largest Element in an Array (2 solutions)
Kth Largest Element in an Array Find the kth largest element in an unsorted array. Note that it is t ...
- Redis学习之路(005)- redis内存数据库C客户端hiredis API 中文说明
A)编译安装 make make install (/usr/local) make install PREFIX=$HOME/progs(可以自由指定安装路径) B)同步的API接口 redisCo ...
- Android App卡顿慢优化之解决内存抖动及内存泄漏
前面一篇博客说到了,内存抖动的第二种情况,就是必须在短时间内创建对象,但是要控制数量:这个问题目前可以使用对象池的方法解决. 3)Object Pools 在程序里面经常会遇到的一个问题是短时间内创建 ...
- appium简明教程(4)——appium client的安装
appium client是对webdriver原生api的一些扩展和封装.它可以帮助我们更容易的写出用例,写出更好懂的用例. appium client是配合原生的webdriver来使用的,因此二 ...
- JAX-RS annotations
@Path("resource_path"):The @Path annotation defines the path to the base URL or resource_p ...
- Spring boot注解(annotation)含义详解
Spring boot注解(annotation)含义详解 @Service用于标注业务层组件@Controller用于标注控制层组件(如struts中的action)@Repository用于标注数 ...
- CommonView for wifi抓包破解WPA无线网络
运行环境:win8 64位+intel 5100n网卡 步骤1:下载CommonView完全破解版,非破解版只有跑10分钟 http://www.nlver.cn/soft/7305.html 步骤2 ...
- 如何在PHP项目中使用phinx进行数据迁移和建表
建表 phinx\bin\phinx.bat migrate -e production 建设 phinx.yml文件 paths: migrations: %%PHINX_CONFIG_DIR%%\ ...