Hive理论基础

  • 2020 年 10 月 10 日
  • 笔记
数仓特征:面向主题,集成,非易失的,时变。数据仓库是在数据库已经大量存在的情况下,为了进一步挖掘数据资源、为了决策需要而产生的,不是所谓的“大型数据库”。
 
数据库与数据仓库的区别(OLTP 与 OLAP 的区别)
  操作型处理,叫联机事务处理 OLTP(On-Line Transaction Processing,),也可以称面向交易的处理系统,它是针对具体业务在数据库联机的日常操作,通常对少数记录进行查询、修改。用户较为关心操作的响应时间、数据的安全性、完整性和并发支持的用户数等问题。传统的数据库系统作为数据管理的主要手段,主要用于操作型处理。
  分析型处理,叫联机分析处理 OLAP(On-Line Analytical Processing)一般针对某些主题的历史数据进行分析,支持管理决策。
 
ETL:抽取 Extra, 转化 Transfer, 装载 Load。
 
为什么要对数仓分层?
分层:Ods、Dw、Dm、Ads
  用空间换时间,通过大量的预处理来提升应用系统的用户体验(效率),因此数据仓库会存在大量冗余的数据;不分层的话,如果源业务系统的业务规则发生变化将会影响整个数据清洗过程,工作量巨大。
 
数仓元数据管理
  元数据(Meta Date), 主要记录数据仓库中模型的定义、各层级间的映射关系、监控数据仓库的数据状态及 ETL 的任务运行状态。可分为技术元数据和业务元数据。
  元数据不仅定义了数据仓库中数据的模式、来源、抽取和转换规则等,而且是整个数据仓库系统运行的基础,元数据把数据仓库系统中各个松散的组件联系起来,组成了一个有机的整体。
 
Hive 是基于 Hadoop 的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类 SQL 查询功能。 本质是将 SQL 转换为 MapReduce 程序。利用HDFS 存储数据,利用 MapReduce 查询分析数据。
 

组件:用户接口,元数据存储mysql / derby,解释、编译、优化、执行器。

 

与数据库的区别

  1. 数据存储位置不同:Hive存储在HDFS中,数据库存储在块设备或本地文件
  2. 数据更新:数仓一般不改写数据,数据库增删改查
  3. 执行延迟:Hive延迟高, mysql延迟低, 只有大规模数据时Hive并行计算的优点才会体现
  4. 数据规模:Hive大规模计算,数据库规模较小
 
三种配置模式(本地模式、远程模式与mysql是否在远程无关!!!)
  1. 内嵌模式:使用的是内嵌的Derby数据库来存储元数据,也不需要额外起Metastore服务。
  1. 本地模式:
  本地模式采用外部数据库来存储元数据,目前支持的数据库有:MySQL、Postgres、Oracle、MS SQL Server.
  不需要单独起metastore服务,用的是跟hive在同一个进程里的metastore服务。也就是说当你启动一个hive 服务,里面默认会帮我们启动一个metastore服务。hive根据hive.metastore.uris 参数值来判断,如果为空,则为本地模式。
  缺点:每启动一次hive服务,都内置启动了一个metastore。本地模式下hive的配置主需要指定mysql的相关信息即可。(ConnectionURL)

  1. 远程模式:
  需要单独起metastore服务,然后每个客户端都在配置文件里配置连接到该metastore服务。远程模式的metastore服务和hive运行在不同的进程里。
  在生产环境中,建议用远程模式来配置Hive Metastore。其他依赖hive的软件都可以通过Metastore访问hive。
  远程模式下,需要配置hive.metastore.uris 参数来指定metastore服务运行的机器ip和端口,并且需要单独手动启动metastore服务。

 
数据模型
  • db(库):在 hdfs 中表现为 hive.metastore.warehouse.dir 目录下一个文件夹
  • table(内部表):在 hdfs 中表现所属 db 目录下一个文件夹,当我们删除一个内部表时,Hive也会删除这个表中数据。内部表不适合和其他工具共享数据。
  • external table(外部表):数据存放位置可以在 HDFS 任意指定路径 ,删除该表并不会删除掉原始数据,删除的是表的元数据
  • partition(分区):在 hdfs 中表现为 table 目录下的子目录
DDL操作:
 1 create table t_user_part(id int,name string,country string) 
 2 partitioned by (guojia string) 
 3 row format delimited fields terminated by ',' ;
 4 --注意顺序问题
 5 --分区的字段不能是表当中的字段
 6 
 7 load data local inpath './root/4.txt' 
 8 into table t_user_part partition (guojia='usa');
 9 
10 load data local inpath '/root/5.txt' 
11 into table t_user_part partition (guojia='china');
12  --将数据加载到哪个文件夹中
13  
14  --多级分区
15 create table t_order(id int,pid int,price double) 
16 partitioned by (year string,month string,day string) 
17 row format delimited fields terminated by ',' ; 
18 
19 load data local inpath '/root/5.txt' 
20 into table t_order partition (year='2019',month='09',day='18');
21 
22 load data local inpath '/root/4.txt' 
23 into table t_order partition (year='2019',month='09',day='18');
24 
25 ALTER TABLE t_user_part ADD PARTITION (guojia='riben') 
26 location '/user/hive/warehouse/hadoop32.db/t_user_part/guojia=riben'; 
27 --一次添加一个分区
28 
29 ALTER TABLE order ADD 
30 PARTITION (year='2018', month='09',day="20") 
31 location'/user/hive/warehouse/hadoop32.db/t_order' 
32 PARTITION (year='2019', month='09',day="20") 
33 location'/user/hive/warehouse/hadoop32.db/t_order';  
34  --一次添加多个分区
35  
36 --删除分区
37 ALTER TABLE t_user_part DROP IF EXISTS PARTITION (guojia=riben);
38 
39 --查看分区
40 show partitions table_name;
41 
42 show formatted table_name;
  • bucket(分桶):在 hdfs 中表现为同一个表目录下根据 hash 散列之后的多个文件 ,采用对列值哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中
DDL操作:
 1 create table stu_buck(Sno string,Sname string,
 2 Sbrithday string, Sex string)
 3 clustered by(Sno) 
 4 into 4 buckets
 5 row format delimited fields terminated by '\t';
 6 --clustered by 根据哪个字段去分桶,这个字段在表中一定存在
 7 --into N buckets 分多少个文件
 8 --如果该分桶字段是string,会根据字符串的hashcode % bucketsNum
 9 --如果该分桶字段是数值类型,数值 % bucketsNum
10 
11 create table student(Sno string,Sname string,
12 Sbrithday string, Sex string) 
13 row format delimited fields terminated by '\t';
14 --insert+select
15 insert overwrite table stu_buck select * from student 
16 cluster by(Sno);
17 --默认不让直接使用分桶表
 
DML操作
 1 --load加载 推荐方式,最常见  (分桶表是不支持load)
 2 load data local inpath '/root/hivedata/students.txt' 
 3 overwrite into table student;
 4 --加载本地数据到表对应的路径下
 5 --local表明是本地还是hdfs
 6 --overwrite表示覆盖操作(慎用)
 7 
 8 load data inpath '/stu' into table student_ext;
 9 --加载hdfs上的文件到表对应的路径下(追加)
10 
11 --insert + select导入
12 --insert 主要是结合 select 查询语句使用,将查询结果插入到表中
13 insert overwrite table tablename1 
14 [partition (partcol1=val1,partclo2=val2)] 
15 select_statement1 from source_table 
16 
17 --多重插入
18 from source_table 
19 insert overwrite table tablename1 
20 [partition (partcol1=val1,partclo2=val2)] 
21 select_statement1 
22 insert overwrite table tablename2 
23 [partition (partcol1=val1,partclo2=val2)] 
24 select_statement2.. 
25 
26 --动态插入 substr(day,1,7) as month,day分区的虚拟字段 顺序需要对应
27 insert overwrite table d_p_t partition (month,day) 
28 select ip,substr(day,1,7) as month,day 
29 from dynamic_partition_table;
30 
31 --指定分隔符(复杂类型的数据表)
32 --表1(包含array字段类型)
33 --数据: zhangsan    beijing,shanghai,tianjin,hangzhou
34 --       wangwu    shanghai,chengdu,wuhan,haerbin
35 create table complex_array(name string,
36 work_locations array<string>) 
37 row format delimited fields terminated by '\t' 
38 collection items terminated by ',';
39 --collection items array集合分隔符
40 
41 --表2(包含map字段类型)
42 create table t_map(id int,name string,hobby map<string,string>)
43 row format delimited 
44 fields terminated by ','
45 collection items terminated by '-'
46 map keys terminated by ':' ;
47 --map keys map中k-v分隔符
48 --数据:1,zhangsan,唱歌:非常喜欢-跳舞:喜欢-游泳:一般般
49 --      2,lisi,打游戏:非常喜欢-篮球:不喜欢
 
DQL操作
4个By区别
  Sort By:分区内有序,只保证每个 reducer 的输出有序,不保证全局有序。
  Order By:全局排序,只有一个Reducer;
  Distrbute By:类似MR中Partition,进行分区,结合sort by使用。
  Cluster By:当Distribute by和Sorts by字段相同时可以使用Cluster by方式。Cluster by除了具有Distribute by的功能外还兼具Sort by的功能。但是排序只能是升序排序,不能指定排序规则为ASC或者DESC。
  如果 distribute 和 sort 的字段是同一个时,此时,cluster by = distribute by + sort by
 
Join
  inner join 内连接,两张表都满足条件的数据
  left join 左链接,以左表为主表,主表的数据都显示
  left semi join 显示左表的数据部分(内连接)
 
参数的配置方式优先级别:依次增强
  默认的配置(hive-default.xml),自定义的配置(hive-site.xml),shell命令行参数,session的命令行中进行设置
Shell命令行参数(常用)   -e “sql” 可以跟上sql的字符串,-f file.sql 可以跟上sql脚本文件
            -hiveconf <property = value> (参数配置,传递参数到脚本文件中)
            -hivevar <key = value> (只能传递参数)

 

内置函数
  查看系统自带的函数:show functions;

  显示自带的函数的用法:

    #不详细 desc function upper;
    #详细 desc function extended upper;
条件判断函数: CASE
语法 : CASE a WHEN b THEN c [WHEN d THEN e]* [ELSE f] END
返回值 : T
说明:如果 a 等于 b ,那么返回 c ;如果 a 等于 d ,那么返回 e ;否则返回 f
举例:hive> Select case 100 when 50 then ‘tom’ when 100 then ‘mary’ else ‘tim’ end from dual;
mary
字符串连接函数:CONCAT
带分隔符字符串连接函数:concat_ws
举例:select concat_ws(‘,’, ‘abc’, ‘123’)
 
自定义函数

  UDF(User-Defined-Function)普通函数 一进一出

继承UDF
重载evaluate方法
打成jar包(胖包)上传到服务器 
将jar包添加到 hive 的 classpath 
    hive>add jar /home/hadoop/udf.jar; 
创建临时函数与开发好的java class关联 
    create temporary function tolowercase as 
    'cn.itcast.hive.UDF_Demo'; 
(不加temporary就是创建永久函数,需要使用drop手动删除)
在hql中使用自定义的函数tolowercase ip  
    Select tolowercase(name),age from t_test; 

  UDAF(User-Defined Aggregation Function)聚合函数 多进一出

UDAF是输入多个数据行,产生一个数据行
用户自定义的UDAF必须是继承了UDAF,且内部包含多个实现了exec的静态类

  UDTF(User-Defined Table-Generating Functions)表生成函数 一进多出

继承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF,
实现initialize, process, close三个方法。
UDTF首先会调用initialize方法,
    此方法返回UDTF的返回行的信息(返回个数,类型)。
初始化完成后,会调用process方法,真正的处理过程在process函数中,
    在process中,每一次forward()调用产生一行;
    如果产生多列可以将多个列的值放在一个数组中,
    然后将该数组传入到forward()函数。
最后close()方法调用,对需要清理的方法进行清理
把程序打成jar包
添加jar包:add jar /run/jar/udf_test.jar;
创建临时函数:
    CREATE TEMPORARY FUNCTION explode_map 
    AS 'cn.itcast.hive.udtf.ExplodeMap';
销毁临时函数:hive> DROP TEMPORARY FUNCTION add_example;

UDTF有两种使用方法,
    一种直接放到select后面(不可以添加其他字段使用,不可以嵌套调用,
        不可以和group by/cluster by/distribute by/sort by一起使用)
    一种和lateral view一起使用
 
lateral view(侧视图)与 explode函数

explode可以对数值类型为array,或者为map结构的进行分割处理

  对array处理:将array每个元素单独作为一行输出
  对map处理:将map中的元素作为一行输出,key作为一列,value作为一列
一般情况下,直接使用即可,也可以根据需要结合lateral view 使用
lateral view为侧视图,意义是为了配合UDTF来使用,把某一行数据拆分成多行数据。不加lateral view的UDTF只能提取单个字段拆分,并不能塞回原来数据表中。加上lateral view就可以将拆分的单个字段数据与原始表数据关联上。在使用lateral view的时候需要指定视图别名和生成的新列别名。
 

1 --select 字段1, 字段2, ... 
2 --from tabelA lateral view UDTF(xxx) 视图别名(虚拟表名) as a,b,c
3 --例如
4 select name,subview.* from test_message 
5 lateral view explode(location) subview as lc;
 
行列转换
1.多行转多列
  col1 col2 col3
  a      c      1
  a      d      2
  a      e      3
  b      c      4
  b      d      5
  b      e      6
现在要将其转化为:
  col1 c d e
  a     1 2 3
  b     4 5 6
此时需要使用到max(case … when … then … else 0 end),仅限于转化的字段为数值类型且为正值的情况
1 select col1,
2 max(case col2 when 'c' then col3 else 0 end) as c,
3 max(case col2 when 'd' then col3 else 0 end) as d,
4 max(case col2 when 'e' then col3 else 0 end) as e
5 from row2col
6 group by col1;
 
2.多行转单列(重要)
  col1 col2 col3
  a      b      1
  a      b      2
  a      b      3
  c      d      4
  c      d      5
  c      d      6
将其转化为:
  col1 col2 col3
  a      b     1,2,3
  c      d     4,5,6
此时需要两个内置的函数:
  a)concat_ws(参数1,参数2),用于进行字符的拼接
    参数1—指定分隔符
    参数2—拼接的内容
  b)collect_set(col3),它的主要作用是将某字段的值进行去重汇总,产生array类型字段,如果不想去重可用collect_list()
 1 select collect_set(col3) from row2col_1;
 2 --将col3的所有数据放到一个集合中(去重)
 3 
 4 select collect_set(col3) from row2col_1 group by col1,col2;
 5 --根据col1,col2进行分组,只有第一列和第二列都相同,认为是同一组
 6 
 7 select col1,col2, collect_set(col3) from row2col_1 
 8 group by col1,col2;
 9 --三列显示,行转列
10 
11 select col1, col2, 
12 concat_ws('', collect_set(cast(col3 as string))) as col3
13 from row2col_1
14 group by col1, col2;
15 --cast(col3 as string)将第三列变成string类型
16 --因为concat_ws是对于字符串拼接
 
3.多列转多行
col1 c d e
a     1 2 3
b     4 5 6
现要将其转化为:
col1 col2 col3
a      c      1
a      d      2
a      e      3
b      c      4
b      d      5
b      e      6
这里需要使用union进行拼接。union 可以结合多个select语句 返回共同的结果集保证每个select语句返回的数据类型个数是一致的。
1 select col1, 'c' as col2, c as col3 from col2row
2 UNION
3 select col1, 'd' as col2, d as col3 from col2row
4 UNION
5 select col1, 'e' as col2, e as col3 from col2row
6 order by col1, col2;
 
4.单列转多行(重要)
col1 col2 col3
a      b     1,2,3
c      d     4,5,6
现要将其转化为:
col1 col2 col3
a      c      1
a      d      2
a      e      3
b      c      4
b      d      5
b      e      6
这里需要使用UDTF(表生成函数)explode(),该函数接受array类型的参数,其作用恰好与collect_set相反,实现将array类型数据行转列。explode配合lateral view实现将某列数据拆分成多行。
1   select col1, col2, lv.col3 as col3
2   from col2row_2 
3   lateral view explode(split(col3, ',')) lv as col3;
 
reflect函数
  可以支持在 sql 中调用 java 中的自带函数,秒杀一切 udf 函数
--例1
--使用 java.lang.Math 当中的 Max 求两列当中的最大值 
select reflect("java.lang.Math","max",col1,col2) from test_udf; 

--例2
--准备数据 test_udf2.txt 
java.lang.Math,min,1,2 
java.lang.Math,max,2,3 
--执行查询 
select reflect(class_name,method_name,col1,col2) from test_udf2; 
 
json
什么叫json:原生的js对象
hive处理json数据总体来说有两个方向的路走:
1. 将json以字符串的方式整个导入hive表,然后通过使用UDF函数解析已经导入到hive中的数据,比如使用lateral view json_tuple的方法,获取所需要的列名
  • get_json_object(string json_string,string path):第一个参数填写json对象变量,第二个参数使用$表示json变量表示,每次只能返回一个数据项
1 select get_json_object(t.json,'$.id'),
2 get_json_object(t.json,'$.total_number') 
3 from tmp_json_test t;
  • json_tuple(string json_string,’属性1′,’属性2′)
1 select json_tuple(json,'id','ids','total_number') 
2 from tmp_json_test;
2. 在导入之前将json拆成各个字段,导入Hive表的数据是已经解析过的,这将需要使用地方放的SerDe
 1 --从http:www.congiu.net/hive-json-serde/下载jar包
 2 add jar 
 3 /root/hivedata/json-serde-1.3.7-jar-with-dependencies.jar;
 4 
 5 create table tmp_json_array(id string,
 6 ids array<string>,total_number int) 
 7 row format SERDE 'org.openx.data.jsonserde.JsonSerDe' 
 8 stored as textfile;
 9 load data local inpath '/root/hivedata/json_test.txt' 
10 overwrite into table tmp_json_array;
 
窗口函数
又叫 OLAP 函数/分析函数,兼具分组和排序功能
窗口函数最重要的关键字是 partition by 和 order by。
具体语法如下:over (partition by xxx order by xxx)
  - 如果不指定 rows between,默认为从起点到当前行;
  - 如果不指定 order by,则将分组内所有值累加;
  - 关键是理解 rows between 含义,也叫做 window 子句:
    - preceding:往前
    - following:往后
    - current row:当前行
    - unbounded:起点
    - unbounded preceding 表示从前面的起点
    - unbounded following:表示到后面的终点
AVG,MIN,MAX,和 SUM 用法一样。
例:
 1 select cookieid,createtime,pv, 
 2 sum(pv) over(partition by cookieid order by createtime) as pv1  
 3 from itcast_t1; 
 4 --pv1: 分组内从起点到当前行的 pv 累积,
 5 --如,11 号的 pv1=10 号的 pv+11 号的 pv, 12 号=10 号+11 号+12 
 6 
 7 select cookieid,createtime,pv, 
 8 sum(pv) over(partition by cookieid) as pv3 
 9 from itcast_t1; 
10 --pv3: 分组内(cookie1)所有的 pv 累加 
11 
12 select cookieid,createtime,pv, 
13 sum(pv) over(partition by cookieid 
14 order by createtime 
15 rows between 3 preceding and 1 following) as pv5 
16 from itcast_t1;
17 --pv5: 分组内当前行+往前 3 行+往后 1 行,
18 --如,14 号=11 号+12 号+13 号+14 号+15 号=5+7+3+2+4=21 
19 
20 select cookieid,createtime,pv, 
21 sum(pv) over(partition by cookieid 
22 order by createtime rows between current row and 
23 unbounded following) as pv6 
24 from itcast_t1; 
25 --pv6: 分组内当前行+往后所有行,
26 --如,13 号=13 号+14 号+15 号+16 号=3+2+4+4=13,
27 --14 号=14 号+15 号+16 号=2+4+4=10 
  • ROW_NUMBER() 从 1 开始,按照顺序,生成分组内记录的序列。 1 2 3 4
  • RANK() 生成数据项在分组中的排名,排名相等会在名次中留下空位 。1 2 2 4
  • DENSE_RANK()生成数据项在分组中的排名,排名相等在名次中不会留下空位。1 2 2 3
1 SELECT  
2 cookieid, 
3 createtime, 
4 pv, 
5 RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn1, 
6 DENSE_RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn2, 
7 ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv DESC) AS rn3  
8 FROM itcast_t2  WHERE cookieid = 'cookie1'; 
NTILE
有时会有这样的需求:如果数据排序后分为三部分,业务人员只关心其中的一部分,如何将这中间的三分之一数据拿出来呢?NTILE 函数即可以满足。 可以看成是:把有序的数据集合平均分配到指定的数量(num)个桶中, 将桶号分配给每一行。如果不能平均分配,则优先分配较小编号的桶,并且各个
桶中能放的行数最多相差 1。 然后可以根据桶号,选取前或后 n 分之几的数据。数据会完整展示出来,只是给相应的数据打标签;具体要取几分之几的数据,需要再嵌套一层根据标签取出。
 1 SELECT * FROM
 2 (SELECT  
 3 cookieid, 
 4 createtime, 
 5 pv, 
 6 NTILE(2) OVER(PARTITION BY cookieid ORDER BY createtime) AS rn1, 
 7 NTILE(3) OVER(PARTITION BY cookieid ORDER BY createtime) AS rn2, 
 8 NTILE(4) OVER(ORDER BY createtime) AS rn3 
 9 FROM itcast_t2  ORDER BY cookieid,createtime) temp
10 WHERE cookieid = 'cookie2' AND  rn2 = 2;

Lag(col, n)往前n行

Lead(col, n)往后n行

 
数据压缩
优缺点
  优点: 减少存储磁盘空间,降低单节点的磁盘 IO。 由于压缩后的数据占用的带宽更少,因此可以快数据在 Hadoop 集群流动的速度,减少网络传输带宽。
  缺点: 需要花费额外的时间/CPU 做压缩和解压缩计算
MR哪些过程可以设置压缩?
  需要分析处理的数据在进入map 前可以压缩,然后解压处理,map 处理完成后的输出可以压缩,这样可以减少网络 I/O(reduce 通常和 map 不在同一节点上),reduce 拷贝压缩的数据后进行解压,处理完成后可以压缩存储在 hdfs 上,以减少磁盘占用量。
 
数据存储格式
  • 行式存储
  优点: 相关的数据是保存在一起,比较符合面向对象的思维,因为一行数据就是一条记录,这种存储格式比较方便进行 INSERT/UPDATE 操作
  缺点: 如果查询只涉及某几个列,它会把整行数据都读取出来,不能跳过不必要的列读取。当然数据比较少,一般没啥问题,如果数据量比较大就比较影响性能 由于每一行中,列的数据类型不一致,导致不容易获得一个极高的压缩比,也就是空间利用率不高 不是所有的列都适合作为索引
  • 列式存储
  优点: 查询时,只有涉及到的列才会被查询,不会把所有列都查询出来,即可以跳过不必要的列查询; 高效的压缩率,不仅节省储存空间也节省计算内存和 CPU。任何列可以作为索引;
  缺点: INSERT/UPDATE 很麻烦或者不方便; 不适合扫描小量的数据
Hive 支持的存储数的格式主要有:TEXTFILE(行式存储) 、SEQUENCEFILE(行式存储)、ORC(列式存储)、PARQUET(列式存储)。
 

TEXTFILE,行式存储,但使用这种方式,hive 不会对数据进行切分,从而无法对数据进行并行操作

 
ORC,列式存储,它并不是一个单纯的列式存储格式,仍然是首先根据行组分割整个表,在每一个行组内进行按列存储
  优点: ORC 是列式存储,有多种文件压缩方式,并且有着很高的压缩比。 文件是可切分(Split)的。因此,在 Hive 中使用 ORC 作为表的文件存储格式,不仅节省 HDFS 存储资源,查询任务的输入数据量减少,使用的 MapTask 也就减少了。 ORC 可以支持复杂的数据结构(比如 Map 等)。ORC 文件也是以二进制方式存储的,所以是不可以直接读取,ORC 文件也是自解析的。
一个 ORC 文件可以分为若干个 Stripe,一个 Stripe可以分为三个部分:
  1. indexData:某些列的索引数据。一个轻量级的 index,默认是每隔 1W 行做一个索引。这里做的索引只是记录某行的各字段在 Row Data 中的 offset
  2. rowData :真正的数据存储。,先取部分行,然后对这些行按列进行存储。对每个列进行了编码,分成多个 Stream 来存储。
  3. StripFooter:存放各个stripe 的元数据信息。每个文件有一个 File Footer,这里面存的是每个 Stripe 的行数,每个 Column的数据类型信息等;每个文件的尾部是一个 PostScript,这里面记录了整个文件的压缩类型以及 FileFooter 的长度信息等。在读取文件时,会 seek 到文件尾部读PostScript,从里面解析到 File Footer 长度,再读 FileFooter,从里面解析到各个Stripe 信息,再读各个 Stripe,即从后往前读。

PARQUET,列式存储,是面向分析型业务的列式存储格式。Parquet 文件是以二进制方式存储的,所以是不可以直接读取的,文件中包括该文件的数据和元数据,因此 Parquet 格式文件是自解析的。 通常情况下,在存储Parquet数据的时候会按照Block大小设置行组的大小,由于一般情况下每一个 Mapper 任务处理数据的最小单位是一个 Block,这样可以把每一个行组由一个 Mapper 任务处理,增大任务执行并行度。

 
存储格式总结
  ORC存储文件默认采用 ZLIB 压缩。比 snappy 压缩的小。 在实际的项目开发当中,hive 表的数据存储格式一般选择:orc 或 parquet。压缩方式一般选择 snappy。
  存储文件的压缩比总结: ORC > Parquet > textFile
  存储文件查询速度三种差不多

 

优化
0. 分区分桶技术,行列过滤
1. Fetch 抓取机制
在 hive-default.xml.template 文件中 hive.fetch.task.conversion 默认是 more,老版本 hive 默认是 minimal,该属性修改为 more 以后,在全局查找、字段查找、limit 查找等都不走 mapreduce。
2. mapreduce 本地模式
mapreduce可以使用本地模拟环境运行,此时就不是分布式执行的程序,但是针对小文件小数据处理特别有效果。用户可以通过设置 hive.exec.mode.local.auto 的值为 true,来让 Hive 在适当的时候自动启动这个优化。
3. join优化
  1)map join 在 Reduce 阶段完成 join。容易发生数据倾斜。可以用 MapJoin 把小表全部加载到内存在 map 端进行 join,避免 reducer处理。 在实际使用中,只要根据业务把握住小表的阈值标准即可,hive 会自动帮我们完成 mapjoin,提高执行的效率。
  2)大表 join 大表
空key过滤,key对应的数据为异常数据,例如空,可进行过滤
空key转换,key对应的数据有用,必须进行join,通过 hive 的 rand 函数,随记的给每一个为空的 id 赋上一个随机值,这样就不会造成数据倾斜。
  3)大小表,小大表join 在当下的 hive 版本中,大表 join 小表或者小表 join 大表,就算是关闭 map端 join 的情况下,基本上没有区别了(hive 为了解决数据倾斜的问题,会自动进行过滤) 。
4. group by 优化—map 端聚合
很多聚合操作都可以先在 Map 端进行部分聚合,最后在 Reduce 端得出最终结果。
  1)是否在 Map 端进行聚合,默认为 True set hive.map.aggr = true;
  2)在 Map 端进行聚合操作的条目数目 set hive.groupby.mapaggr.checkinterval = 100000;
  3)有数据倾斜的时候进行负载均衡(默认是 false) set hive.groupby.skewindata = true;
5. 数据倾斜问题
  1)调整mapTask个数
在Map执行前合并小文件,减少Map数:CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)。HiveInputFormat没有对小文件合并功能。
当 input 的文件都很大,任务逻辑复杂,map 执行非常慢的时候,可以考虑增加 Map 数
  2)调整reduceTask个数,reduce 个数并不是越多越好
    1)过多的启动和初始化 reduce 也会消耗时间和资源;
    2)另外,有多少reduce,就会有多少输出文件,如果生成很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题; 在设置 reduce 个数的时候也需要考虑这两个原则:处理大数据量利用合适的 reduce 数;使单个 reduce 任务处理数据量大小要合适。
6. 了解执行计划—explain
7. 并行执行机制
通过设置参数 hive.exec.parallel 值为true,就可以开启并发执行。
8. 严格模式
通过设置属性 hive.mapred.mode 值为默认是非严格模式 nonstrict 。开启严格模式需要修改 hive.mapred.mode 值为 strict,开启严格模式可以禁止 3 种类型的查询。
  1)对于分区表,除非 where 语句中含有分区字段过滤条件来限制范围,否
则不允许执行。用户不允许扫描所有分区。
  2)对于使用了 order by 语句的查询,要求必须使用 limit 语句。因为 order
by 为了执行排序过程会将所有的结果数据分发到同一个 Reducer 中进行处理,
  3)限制笛卡尔积的查询。
9. jvm 重用机制
JVM 重用可以使得 JVM 实例在同一个 job 中重新使用 N 次,这个功能的缺点是,开启 JVM 重用将一直占用使用到的 task 插槽,以便进行重用,直到任务完成后才能释放。
10. 推测执行机制
推测出“拖后腿”的任务,并为这样的任务启动一个备份任务,让该任务与原始任务同时处理同一份数据,并最终选用最先成功运行完成任务的计算结果作为最终结果。