1. join时将大表放后,小表放在前
正确的说法:把重复关联键少的表放在join前面做关联可以提高join的效率,实际操作中也没法看什么重复连接键多少,因此一般都是小表在前了,表越少,重复的连接键总量就越少。
因此通常需要将小表放前面,或者标记哪张表是大表:/*streamtable(table_name) */
https://blog.csdn.net/qq_26442553/article/details/80865014?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~sobaiduend~default-3-80865014.nonecase&utm_term=hive%20%E5%B0%8F%E8%A1%A8%E5%9C%A8%E5%89%8D%E7%9A%84%E5%8E%9F%E7%90%86&spm=1000.2123.3001.4430
2. 多表join时,使用相同的连接键
当对3个或者更多个表进行join连接时,如果每个on子句都使用相同的连接键的话,那么只会产生一个MapReduce job。因为,连接键一样,直接所有的map阶段的key就相同,只需要将所有表的key,按照hash分区,在reduce阶段聚合就行了,最后生成的结果就是总的结果。再者,reduce阶段的作用就是对相同的key进行操作的。若不一样,只能生成几个mapreduce分别处理。
3. 尽量尽早地过滤数据,查询时也要注意数据过率,避免全表扫描
减少每个阶段的数据量,对于分区表要加分区,同时只选择需要使用到的字段。
列处理:在 SELECT 中,只拿需要的列,如果有,尽量使用分区过滤,少用 SELECT *。
行处理:在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在 Where 后面,那么就会先全表关联,之后再过滤。
SELECT a.id
FROM lxw1234_a a
left outer join t_lxw1234_partitioned b
ON (a.id = b.url);
WHERE b.day = ‘2015-05-10′
正确的写法是写在ON后面:
SELECT a.id
FROM lxw1234_a a
left outer join t_lxw1234_partitioned b
ON (a.id = b.url AND b.day = ‘2015-05-10′);
或者直接写成子查询:
SELECT a.id
FROM lxw1234_a a
left outer join (SELECT url FROM t_lxw1234_partitioned WHERE day = ‘2015-05-10′) b
ON (a.id = b.url)
4. 尽量原子化操作
尽量避免一个SQL包含复杂逻辑,可以使用中间表来完成复杂的逻辑
5. MapJoin避免数据倾斜
如果不指定 MapJoin 或者不符合 MapJoin 的条件,那么 Hive 解析器会将 Join 操作转换成 Common Join,即:在 Reduce 阶段完成 join。容易发生数据倾斜。可以用 MapJoin 把小表全部加载到内存在 map 端进行 join,避免 reducer 处理。
6. 并行执行
hive会将一个查询转化为一个或多个阶段,包括:MapReduce阶段、抽样阶段、合并阶段、limit阶段等。默认情况下,一次只执行一个阶段。 不过,如果某些阶段不是互相依赖,是可以并行执行的。
set hive.exec.parallel=true,可以开启并发执行。
set hive.exec.parallel.thread.number=16; //同一个sql允许最大并行度,默认为8。
会比较耗系统资源
7、调整mapper和reducer的个数
(1)map阶段优化:
map个数的主要的决定因素有: input的文件总个数,input的文件大小,集群设置的文件块大小(默认128M,不可自定义)。
举例: a) 假设input目录下有1个文件a,大小为780M,那么hadoop会将该文件a分隔成7个块(6个128m的块和1个12m的块),从而产生7个map数
b) 假设input目录下有3个文件a,b,c,大小分别为10m,20m,130m,那么hadoop会分隔成4个块(10m,20m,128m,2m),从而产生4个map数 即,如果文件大于块大小(128m),那么会拆分,如果小 于 块大小,则把该文件当成一个块。
map执行时间:map任务启动和初始化的时间+逻辑处理的时间。
是不是 map 数越多越好?答案是否定的。如果一个任务有很多小文件(远远小于块大小 128m),则每个小文件也会被当做一个块,用一个 map 任务来完成,而一个 map 任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的 map 数是受限的。
是不是保证每个 map 处理接近 128m 的文件块,就高枕无忧了?答案也是不一定。比如有一个 127m 的文件,正常会用一个 map 去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果 map 处理的逻辑比较复杂,用一个 map任务去做,肯定也比较耗时。
针对上面的问题,我们需要采取两种方式来解决:即减少 map 数和增加 map 数;
1)减少map数
若有大量小文件(小于128M),会产生多个map,处理方法是:
set mapred.max.split.size=100000000;
set mapred.min.split.size.per.node=100000000;
set mapred.min.split.size.per.rack=100000000;
-- 前面三个参数确定合并文件块的大小,大于文件块大于128m的,按照128m来分隔,小于128m,大于100m的,按照100m来分隔,把那些小于100m的(包括小文件和分隔大文件剩下的)进行合并
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; -- 执行前进行小文件合并
2)增加map数
当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率。
set mapred.reduce.tasks=?
(2)reduce阶段优化:
Reduce 个数并不是越多越好?
过多的启动和初始化 Reduce 也会消耗时间和资源;
另外,有多少个 Reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;在设置 Reduce 个数的时候也需要考虑这两个原则:处理大数据量利用合适的 Reduce数;使单个 Reduce 任务处理数据量大小要合适;
调整方式:
-- set mapred.reduce.tasks=?
-- set hive.exec.reducers.bytes.per.reducer = ?
一般根据输入文件的总大小,用它的estimation函数来自动计算reduce的个数:reduce个数 = InputFileSize / bytes per reducer
// 输出合并小文件
SET hive.merge.mapfiles = true; -- 默认 true,在 map-only 任务结束时合并小文件 SET hive.merge.mapredfiles = true; -- 默认 false,在 map-reduce 任务结束时合并小文件 SET hive.merge.size.per.task = 268435456; -- 默认 256M SET hive.merge.smallfiles.avgsize = 16777216; -- 当输出文件的平均大小小于该值时,启动一个独立的 map-reduce 任务进行文件 merge
8、采用 分区 技术
在查询时候指定分区,可以避免全表扫描,其实就是到指定的分区文件下去查找想要的数据,这样就提升了效率。
9、采用 分桶 技术
分桶是对分区的进一步的划分。分桶将整个数据内容按照某列属性值的hash值进行区分,如要按照name属性分为3个桶,就是对name属性值的hash值对3取摸,按照取模结果对数据分桶。如取模结果为0的数据记录存放到一个文件,取模为1的数据存放到一个文件,取模为2的数据存放到一个文件。
提升Join查询的效率,若两个表都在连接的字段上进行了分桶,那么在join的时候可以使用 Map 端连接 (Map-side join)高效的实现。比如Join 操作。对于Join 操作两个表有一个相同的列,如果对这两个表都进行了分桶操作。那么将保存相同列值的桶进行Join 操作就可以,可以大大减少Join 数据量。
分区和分桶:https://www.cnblogs.com/guoyu1/p/12123088.html
参考博客:https://www.cnblogs.com/1130136248wlxk/articles/5517666.html