- Join 优化
- 大表放右边,小表放左边。
- 多个表关联时,最好分拆成小段,避免大sql
- 大表 join 大表可采用 空 key 过滤、空key转换随机赋值
- 使用 mapjoin
开启 mapjoin 设置:
- 设置自动选择Mapjoin,默认为true
set hive.auto.convert.join = true;
- 大表小表的阈值设置(默认25M以下认为是小表):
set hive.mapjoin.smalltable.filesize=25123456;
- 控制 Reducer 数量
Reduce 的最大值可以设为0.95 * n
(n 为 NodeManager 数量)。
决定Hive 作业的 Reducer 个数
有下面几个参数
# Hive 0.14.0 以后默认为 256MB,以前默认为 1G
hive.exec.reducers.bytes.per.reducer = 256000000
# Hive 0.14.0 以后默认为 1009 ,以前默认为 999
hive.exec.reducers.max = 1009
# 如果 mapred.job.tracke r的值是 local 此参数将会被忽略。在Hadoop中此参数的默认值是1,而在Hive中默认值是-1。通过将此参数设置为-1,Hive将自动计算出应该启动多少个Reduce。
mapred.reduce.tasks/mapreduce.job.reduces = -1
Reducer 的个数的计算公式
Reducer 的个数 = min( 参数2, 总输入数据量 / 参数1)
说明: input size/hive.exec.reducers.bytes.per.reducer > hive.exec.reducers.max,那么Hive启动的Reduce 个数为 hive.exec.reducers.max ;反之为 input size/hive.exec.reducers.bytes.per.reducer 。这个参数只有在mapred.reduce.tasks / mapreduce.job.reduces 设置为负数的时候才有效。
注意:
- Reduce 的个数对整个作业的运行性能有很大影响。
如果 Reduce 设置的过大,那么将会产生很多小文件,对 NameNode 会产生一定的影响,而且整个作业的运行时间未必会减少,过多的启动和初始化 reduce 也会消耗时间和资源;
如果Reduce设置的过小,那么单个Reduce处理的数据将会加大,很可能会引起 OOM 异常。 - 如果设置了 mapred.reduce.tasks/mapreduce.job.reduces 参数,那么Hive会直接使用它的值作为 Reduce 的个数;
- 如果 mapred.reduce.tasks/mapreduce.job.reduces 的值没有设置(也就是-1),那么Hive会根据输入文件的大小估算出 Reduce 的个数。
根据输入文件估算 Reduce 的个数可能未必很准确,因为 Reduce 的输入是Map的输出,而 Map 的输出可能会比输入要小,所以最准确的数根据 Map 的输出估算 Reduce 的个数。
设置reduce个数原则
需要考虑这两个原则
1. 处理大数据量使用合适的reduce数;
2. 使单个 reduce 任务处理数据量大小合适。
- 开启列裁剪
开启 列裁剪可以减小读取开销。
# 默认为 true (开启)
set hive.optimize.cp = true
例:
表 test 有 a,b,c,d,e 列,开启列裁剪后,执行如下 HQL 语句
SELECT a,b from test where a < 10;
Hive 不会读取 c,d,e 列的数据,减小读取开销
。
- 分区裁剪
开启分区裁剪可以在查询过程中减少不必要的分区,从而减少数据的读入。
# 默认为 true (开启)
set hive.optimize.pruner = true
例:
SELECT * FROM (SELECT c1, count() FROM T GROUP BY c1) subq WHERE subq.prtn = 100;
SELECT * FROM T1 JOIN (SELECT * FROM T2) subq ON (T1.c1 = subq.c2) subq.prtn = 100;
上述语句会在子查询中就考虑 subq.prtn = 100 条件,从而减少读入的分区数目。
- Group By 优化
- 开启 Map 端聚合
启用 map 端部分聚合,其思想和combine类似,这样可机减少要排序并分发到 reducer 的数据。
# 默认为 true
hive.map.aggr = true
# Map 端进行聚合操作的条目数目
hive.groupby.mapaggr.checkinterval = 10000
- 合并小文件
文件数目过多,会给 HDFS 带来压力,可以通过合并 Map 和 Reduce 的输出文件来减少文件数。
# 是否合并 Map 阶段的输出文件,默认为 true
hive.merge.mapfiles = true
# 是否合并 Reduce 阶段的输出文件,默认为 false
hive.merge.mapredfiles = true
# 合并后所需的文件大小,默认 256 MB
hive.merge.size.per.task = 256 000 000
- Multi-Group By 和 Multi-Insert
Hive 的 Multi-Group By 和 Multi-Insert 特有的语法可以在同意个查询语句中使用多个不想交的
Insert 语句,这样分开使用多个 Inert 语句效率高,因为只需要扫描一遍全表。
FROM test
INSERT overwrite TABLE test_by_a
SELECT a, count(e) GROUP BY a
INSERT overwrite TABLE test_by_b
SELECT b, count(f) GROUP BY b
INSERT overwrite TABLE test_by_c
SELECT c, count(g) GROUP BY c ;
使用上述特性需要将 FROM 子句放在最前面。
- 利用 UNION ALL 特性
利用 UNION ALL 特性将多个 MapReduce 作业合并。
SELECT * FROM
(
SELECT * FROM t1 GROUP BY c1,c2,c3
UNION ALL
SELECT * FROM t2 GROUP BY c1,c2,c3
) t3 GROUP BY c1,c2,c3 ;
上述 HQL 包含3个MapReduce 作业,转化为下面的 HQL 只进行一个 MapReduce作业。
SELECT * FROM
(
SELECT * FROM t1
UNION ALL
SELECT * FROM t2
) t3 GROUP BY c1,c2,c3 ;
- 并行执行
Hive 默认是不考虑并行性的,会依次执行作业。可以通过数 hive.exec.parallel 开启并行执行模式。
# true 为开启,默认为 false 不开启
hive.exec.parallel = true
一个 HQL 会被 Hive 拆分成多个 MapReduce 作业,这些作业之间有时会相互依赖,有时会相互独立。
如表1和表2 join的结果与表3和表4 join的结果在join ,那么表1和表2 join 与表3和表4 join这两个作业相互独立,而最后一次 join 作业则依赖于前两个作业,从逻辑上来看,第一个作业和第二个作业可以同时进行。
- 全排序
当执行 ORDER BY 操作时,所有的数据将会集中到 Reducer 里面做一次全排序,当执行 SORT BY 时,则是进行局部排序。可以使用TotalOrderPartitioner
将全排序变成局部排序。
首先,指定使用的 Partitioner 和分发区间文件。
set hive.mapred.partitioner = org.apache.hadoop.mapred.lib.TotalOrderPartitioner;
set total.order.partitioner.path = /tmp/range_key;
/tmp/range_key 文件是一个 SequeceFile 格式文件,它指定了数据的分发区间。
生成分发文件的区间可以通过分桶抽样的形式生成,如下:
# 生成/tmp/range_key 文件
CREATE EXTERNAL TABLE range_keys(id int)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.binarysortable.BinarySortableSerDe'
STORED AS
INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat'
OUTPutFORMAT 'org.apache.hadoop.hive.ql.io.HiveNullValue SequeceFileOutputFormat'
LOCATION '/tmp/range_key';
INSERT OVERWRITE TABLE range_keys
SELECT DISTINCT id
FROM SOURCE t_sale sampletable (Bucket 100 OUT 0f 100 ON rand())s
SORT BY id;
- Top N
使用SORT BY ... LIMIT N
方式,求取 Top N。
如果使用ORDER BY ... LIMIT N
的话,该 HQL 只会生成一个作业,所有的数据将会集中到一个 Reduce 中进行全排序,这样效率非常低。
如果使用SORT BY ... LIMIT N
的方式,Hive 会生成两个作业,在第一个作业中按照SORT BY
的排序方式,在第二个作业的 Reduce中,将接收到 M * N 条数据进行排序再取 Top N。