这里写目录标题
hive数据存储与压缩
数据的存储格式:
两大类:行式存储 列式存储
行式存储两种:textFile sequenceFile
列式存储两种:parquet orc
一般原始数据,别人给过来的,都是textfile这种形式的
经过分析之后通过insert overwrite select 将我们分析出来的结果插入到另外一张临时表里面就可以使用parquet 或者orc这些列式存储格式
在实际的项目开发当中,hive表的数据存储格式一般选择:orc或parquet。压缩方式一般选择snappy。
ORC存储方式默认ZLIB压缩方式
1)创建一个非压缩的的ORC存储方式
//建表
create table log_orc_none(
track_time string,
url string,
session_id string,
referer string,
ip string,
end_user_id string,
city_id string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS orc tblproperties ("orc.compress"="NONE");
//插入数据
insert into table log_orc_none select * from log_text ;
//查看插入后数据
dfs -du -h /user/hive/warehouse/myhive.db/log_orc_none;
2)创建一个SNAPPY压缩的ORC存储方式
//建表
create table log_orc_snappy(
track_time string,
url string,
session_id string,
referer string,
ip string,
end_user_id string,
city_id string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS orc tblproperties ("orc.compress"="SNAPPY");
//插入数据
insert into table log_orc_snappy select * from log_text ;
//查看插入后数据
dfs -du -h /user/hive/warehouse/myhive.db/log_orc_snappy ;
调优
Fetch抓取
能不走mr的就尽量不走mr。
hive.fetch.task.conversion 可配置取值 none, minimal, more
默认值是 more
如果设置成none,所有的都要走mr select * 也要走
下面这些都不会执行mapreduce程序。
hive (default)> set hive.fetch.task.conversion=more;
hive (default)> select * from score;
hive (default)> select s_score from score;
hive (default)> select s_score from score limit 3;
hive本地模式
可以限制我们输入数据的个数小于多少个。
当有大量小文件,造成分配资源时间远超过数据处理时间时,可以采用hive本地模式。
限制我们输入的数据量小于一定值时,启用本地模式运行,所有数据都在一个maptask,一个reducetask里面处理。
用户可以通过设置hive.exec.mode.local.auto的值为true,来让Hive在适当的时候自动启动这个优化。
set hive.exec.mode.local.auto=true; //开启本地mr
//设置local mr的最大输入数据量,当输入数据量小于这个值时采用local mr的方式,默认为134217728,即128M
set hive.exec.mode.local.auto.inputbytes.max=51234560;
//设置local mr的最大输入文件个数,当输入文件个数小于这个值时采用local mr的方式,默认为4
set hive.exec.mode.local.auto.input.files.max=10;
//开启本地模式,并执行查询语句
hive (default)> set hive.exec.mode.local.auto=true;
hive (default)> select * from score cluster by s_id;
18 rows selected (1.568 seconds)
//关闭本地模式,并执行查询语句
hive (default)> set hive.exec.mode.local.auto=false;
hive (default)> select * from score cluster by s_id;
Hadoop中可通过mapred-site.xml,配置小任务模式
<name>mapreduce.job.ubertask.enable</name>
<value>true</value>
表的优化
Join
早期的hive版本
小表join大表 会将小表的数据一次性进入到内存当中去,与右边大表进行匹配
现在的版本已经不存在这一条优化了,hive会有一个优化器,经过自动选择,可以自动的找出哪个表是小表,将小表的数据一次性放到内存
select count(distinct s_id) from score;
select count(1) from (
select count(s_id) from score group by s_id
) temp;
由于mapTask远多于Reduce,一个reduce端要处理很多数据,所以先在map端对数据进行一次聚合,然后发送到reduce数据会变少,然后再count的时候就会比较快。
多个表关联时,最好分拆成小段,避免大sql(无法控制中间Job,都在写一起不知道具体哪一步慢)
大表Join大表,尽量的减少输入的数据量
- 空KEY过滤
有时join超时是因为某些key对应的数据太多,而相同key对应的数据都会发送到相同的reducer上,从而导致内存不够。此时我们应该仔细分析这些异常的key,很多情况下,这些key对应的数据是异常数据,我们需要在SQL语句中进行过滤。例如key对应的字段为空,操作如下:
不过滤:
INSERT OVERWRITE TABLE jointable
SELECT a.* FROM nullidtable a JOIN ori b ON a.id = b.id;
过滤:
INSERT OVERWRITE TABLE jointable
SELECT a.* FROM (SELECT * FROM nullidtable WHERE id IS NOT NULL ) a JOIN ori b ON a.id = b.id;
- 空key转换
有时虽然某个key为空对应的数据很多,但是相应的数据不是异常数据,必须要包含在join的结果中,此时我们可以表a中key为空的字段赋一个随机的值,使得数据随机均匀地分不到不同的reducer上。
不随机分布:
set hive.exec.reducers.bytes.per.reducer=32123456;
set mapreduce.job.reduces=7;
INSERT OVERWRITE TABLE jointable
SELECT a.*
FROM nullidtable a
LEFT JOIN ori b ON CASE WHEN a.id IS NULL THEN 'hive' ELSE a.id END = b.id;
随机分布:
set hive.exec.reducers.bytes.per.reducer=32123456;
set mapreduce.job.reduces=7;
INSERT OVERWRITE TABLE jointable
SELECT a.*
FROM nullidtable a
LEFT JOIN ori b ON CASE WHEN a.id IS NULL THEN concat('hive', rand()) ELSE a.id END = b.id;
MapJoin
新版本当中小表join大表,以及大表join小表没有区别了
开启map端的join功能
set hive.auto.convert.join = true; 默认为true
Group By
有很多时候数据都可以在map端先进行一次聚合 类似于规约
先进行分组,再聚合,可以用两个mapreduce来完成。
Count(distinct)
直接去重,只能有一个reducetask,数据处理会比较慢
SELECT count(DISTINCT id) FROM bigtable;
select count(1) from (
SELECT count(id) FROM (SELECT id FROM bigtable GROUP BY id) a
) tempTable
虽然多一个mapreduce,但是速度会变快。
笛卡尔积
任何时候都要避免笛卡尔积
join的时候一定要避免无效的on条件
使用分区剪裁、列剪裁
只取我们用到的分区,只取我们用到的字段
不要写select * 对于分区表,一定要带上分区条件
减少数据的输入量
SELECT a.id
FROM bigtable a
LEFT JOIN ori b ON a.id = b.id
WHERE b.id <= 10;
//先过滤,再join
SELECT a.id
FROM ori a
LEFT JOIN bigtable b ON (a.id <= 10 AND a.id = b.id);
SELECT a.id
FROM bigtable a
RIGHT JOIN (
SELECT id
FROM ori
WHERE id <= 10
) b ON a.id = b.id;
动态分区调整
适用于分区表数据。以第一个表的分区规则,来对应第二个表的分区规则,将第一个表的所有分区,全部拷贝到第二个表中来,第二个表在加载数据的时候,不需要指定分区了,直接用第一个表的分区即可
- 开启动态分区功能(默认true,开启)
set hive.exec.dynamic.partition=true; - 设置为非严格模式(动态分区的模式,默认strict,表示必须指定至少一个分区为静态分区,nonstrict模式表示允许所有的分区字段都可以使用动态分区。)
set hive.exec.dynamic.partition.mode=nonstrict; - 在所有执行MR的节点上,最大一共可以创建多少个动态分区。
set hive.exec.max.dynamic.partitions=1000; - 在每个执行MR的节点上,最大可以创建多少个动态分区。该参数需要根据实际的数据来设定。比如:源数据中包含了一年的数据,即day字段有365个值,那么该参数就需要设置成大于365,如果使用默认值100,则会报错。
set hive.exec.max.dynamic.partitions.pernode=100 - 整个MR Job中,最大可以创建多少个HDFS文件。
在linux系统当中,每个linux用户最多可以开启1024个进程,每一个进程最多可以打开2048个文件,即持有2048个文件句柄,下面这个值越大,就可以打开文件句柄越大
set hive.exec.max.created.files=100000; - 当有空分区生成时,是否抛出异常。一般不需要设置。
set hive.error.on.empty.partition=false;
不用动态分区的时候插入数据,需要手动的指定分区值
insert overwrite table ori_partitioned_target PARTITION (p_time=‘20130812’)
select id, time, uid, keyword, url_rank, click_num, click_url from ori_partitioned;
使用动态分区,不用手动指定分区值,但是select字段的时候,最后面的字段,一定要是分区字段
分区字段的值不能是中文
INSERT overwrite TABLE ori_partitioned_target PARTITION (p_time)
SELECT id, time, uid, keyword, url_rank, click_num, click_url, p_time
FROM ori_partitioned;
分桶
将大文件,按照一定的规则,划分成多个小文件
数据倾斜
map个数:block块大小有关,一个block块对应一个maptask
reduce个数: job.setNumReduceTasks(10) 手动指定
map个数
map和reduce的个数并不是越多越好。
如果有一个block块,里面有几千万条记录,map处理比较复杂。
如何减少map个数?
- 调整block块的大小
- 合并小文件
set mapred.max.split.size=112345600;
set mapred.min.split.size.per.node=112345600;
set mapred.min.split.size.per.rack=112345600;
set hive.input.format= org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
这个参数表示执行前进行小文件合并,前面三个参数确定合并文件块的大小,大于文件块大小128m的,按照128m来分隔,小于128m,大于100m的,按照100m来分隔,把那些小于100m的(包括小文件和分隔大文件剩下的),进行合并。大于128M的文件,按照128M来进行切分,100到128按照100进行切分。切分剩下的小文件,进行合并。
如何增加map个数?
可以使用distributed by来先将文件计算一遍,打散成很多的小文件
set mapreduce.job.reduces =10;
create table a_1 as
select * from a
distribute by rand(123);
将我们的表数据,均匀的打散成十个小文件,这样就最少会有10个maptask
reduce个数
-
调整reduce个数方法一
1)每个Reduce处理的数据量默认是256MB
hive.exec.reducers.bytes.per.reducer=256123456
2)每个任务最大的reduce数,默认为1009
hive.exec.reducers.max=1009
3)计算reducer数的公式
N=min(参数2,总输入数据量/参数1) -
调整reduce个数方法二
在hadoop的mapred-default.xml文件中修改
设置每个job的Reduce个数
set mapreduce.job.reduces = 15;
jvm重用
container执行完了maptask或者reducetask之后,不要释放资源
继续给下一个maptask或者reduetask进行执行。
在hive当中设置jvm重用
set mapred.job.reuse.jvm.num.tasks=10;