Hive介绍
hive是基于 Hadoop平台操作 HDFS 文件的插件工具
可以将结构化的数据文件映射为一张数据库表
可以将 HQL 语句转换为 MapReduce 程序
1.hive 是由驱动器组成,驱动器主要由4个组件组成(解析器、编译器、优化器、执行器)
2.hive本身不存储数据,数据是存储在hdfs上
3.hive的元数据默认是存储在detby数据库中,但是它支持一个客户端进行连接,为了支持多客户端连接,可将元数据存储在关系型数据库中(如mysql)
4.hive本身不参与数据计算,数据计算交由计算引擎,hive支持的计算引擎由3种(MapReduce默认、Tez、Spark )
库
Hive的数据都是存储在 hdfs 上的,默认有一个根目录。由文件 hive-site.xml 中的参数是hive.metastore.warehouse.dir指定。默认为 /user/hive/warehouse
比如名为itcast的数据库存储路径为:/user/hive/warehouse/itcast.db
表
表所对应的数据是存储 在HDFS上的,而表相关的元数据是存储在关系型数据库中
hive有两种类型的表:内部表(默认表)和外部表
1.内部表:在创建表的时候没有设置external关键字,则创建的是一张内部表
表结构中:table_type:managed_table
特点:在删除表的时候,不光删除表结构,会同步将hdfs上对应的文件夹一起删除。
使用场景:由于内部表在删除表的时候会将数据文件同步删除,所以在实际的工作中,一些核心表是不会采用内部表,
如果要临时保存一些数据,或者是一些临时的计算结果则可以使用内部表
2.外部表:在创建表的时候指定了external关键字,则创建的属于一张外部表
表结构中:table_type:external_table
特点:在删除表的时候,只删除表结构,不会删除hdfs上对应的文件夹
使用场景:在实际的工作中,绝大部分的表都会采用的是外部表。
修改外部表emp为内部表(emp为表名)
alter table emp set tblproperties ('EXTERNAL'='FALSE');
修改内部表emp为外部表
alter table emp set tblproperties ('EXTERNAL'='TRUE');
内部表和外部表之间允许相互转换
语法:alter table 表名 set tblproperties('EXTERNAL'='FALSE|TRUE')
EXTERNAL=FALSE:代表设置为内部表
EXTERNAL=TRUE:代表设置为外部表
如果要删除外部表的数据文件方式:
1.先将表结构转换成内部表,然后删除内部表
2.先删除表结构,然后使用hdfs的命令来删除:hdfs dfs -rm -r 文件地址
分区
分区是hive的一种优化手段。分区是指根据表的字段(例如“日期day”)将表划分为不同分区
一个分区表示一个文件夹
分区:将一个大的文件夹进行拆分
分桶:将一个大的文件进行分桶
hive 静态分区
静态分区
指的是分区字段值在加载数据时,是由用户手动指定的
创建分区表
(1)关键语句:
partitioned by (field string)
(2)分区字段不能是表中已经存在
的字段
(3)分区字段是虚拟字段,其数据并不存储在底层的文件中
(4)分区不同,对应数据存放的文件夹不同
静态分区:手工分区,分区的名称和分区额数据都是用户来直接添加,并且一次只能向一个分区中添加数据。
添加数据的方式:
1.load data [local] inpath '文件地址' into table 表名 partition(分区列名=值)
--静态分区数据加载
load data local inpath '/root/EMP.txt' into table emp_partition partition (deptno=20);
2.insert into|overwrite table 表名 partition(分区列名=值) select 列名,列名,....from 表;
如果是静态分区加载数据,使用insert into则查询出来的数据中不能包含分区列的值。
~单分区查询
select * from emp_partition where dept=20;
~多分区查询
select * from emp_partition where dept=20unionselect * from emp_partition where dept=30
增加分区
alter table emp_partitionadd partition(dept=40);
hive msck 修复分区
dfs -mkdir -p /user/hive/warehouse/emp_partition/dept=50;
1.上传数据到分区
dfs -put /opt/modules/input/emp.txt /user/hive/warehouse/emp_partition/dept=50;
2.查询数据
select * from emp_partition where dept=50;
3.修复关系:
msck repair table emp_partition;
删除分区数据
-- 这将删除该分区的数据和元数据
alter table emp_partition drop partition(dept=50);
- 删除多个分区
alter table dept_partition drop partition ( month = '2021-02-11' ), partition(month = '2021-02-12');
alter table dept_partition drop partition ( month <= '2021-02-12' );
重命名分区表名
-- dept_partition 旧表名 ---> dept_partition2 是新表名
alter table emp_partition rename to emp_partition2;
更改分区文件存储格式
alter table table_name partition (dt='2008-08-09') set fileformat file_format;
更改分区位置
alter table table_name partition (dt='2008-08-09') set location "new location";
hive 动态分区
动态分区
指的是分区的字段值是基于查询结果自动推断出来的。要借助核心语法(
insert+select)
开启动态分区功能(默认 true,开启)
hive (default)> set hive.exec.dynamic.partition=true;
设置为非严格模式
- 动态分区的模式,默认 strict,表示必须指定至少一个分区为静态分区
- nonstrict 模式表示允许所有的分区字段都可以使用动态分区
hive (default)>set hive.exec.dynamic.partition.mode=nonstrict;
1.创建分区表
create table dept_par
(deptno string, -- 部门编号dname string -- 部门名
)partitioned by (change_dt string) -- 部门改头换面日期row format delimited fields terminated by '\t';
2、从 表dept 中查询数据,动态插入到 表dept_par 的不同分区中
insert into table dept_par partition (change_dt)
select deptno ,dname ,change_dt
from dept;
① 从表dept 中查询出 列deptno,列dname 中的数据,然后插入到 表dept_par 中
② 再从 表dept 中 查询出 列change_dt,让这一列作为 表dept_par 的分区字段
Hive 分桶
分桶表 分区表 区别与联系
分区针对的是数据的存储路径
(分区是根据表的某一列得到的,分区不同,HDFS上的文件夹不同);分桶针对的是数据文件
(分桶是根据表的某一列下数据值,经hash取余得到的)
分区字段不能是
表中已经存在的字段;分桶的字段必须是
表中已经存在的字段
2者都是hive的一种优化手段,为了提高查询效率
设置分桶
create [external] table 表名(列名,......)partitioned by (....)clustered by (分桶列) into n buckets......
注意:1.分桶的列是从表中的列中选出来一个作为分桶列,不需要单独定义。
2.n代表数字,表示要分的桶的数量
3.在加载数据的时候如果没有设置reduce的数量则默认reduce的数量就和桶的数量是一致的。
4.如果处理数据的时候没有设置reduce的数量,默认1个reduce,但是如果reduce处理的数据量超过1G,则系统会启动新的reduce
如果设置了reduce的数量,则处理数据的reduce就是设置的数量
分桶建议:1.选择分桶列的时候尽量选择关联的列进行分桶,这样可以提高表的关联效率。
2.选择桶的数量的时候尽量使用质数,这样可以减少数据倾斜的发生。
3.尽量在大文件中进行分桶。
4.分桶列尽量选择一些重复率比较低的列,可以减少数据与倾斜。
分桶规则:先将分桶的列的值计算hash值,然后使用改hash值对桶的数量进行取余,余数就是对应的数据所在的桶的位置,0代表第一桶。
分桶表的优点
-
基于分桶字段查询时,减少全表扫描
-
JOIN时可以提高MR程序效率,减少笛卡尔积数量
分桶抽样
抽样:从所有的样本中抽取一部分的数据作为样本来进行进行数据分析和调试。。
注意:1.抽样可以在任何表中进行,不限制于分桶表。
2.分桶抽样其实就是在查询数据的时候对数据按照指定的列,进行逻辑分桶,不受原来表中是否分桶限制,可以重新设置分桶的列以及分桶的数量
抽样的语法:
select * from 表 tablesample(bucket n out of m [on 分桶列]);
n:代表获取的第几桶数据
m:代表桶的数量
on用来设置分桶抽样的时候分桶列,如果在一个分桶表上进行抽样,则可以将on省略,默认使用分桶表的分桶列,
如果在一个没有分桶的表上进行分桶抽样,则on是不能省略。
OLTP 和 OLAP 的区别
OLTP(on-line transaction processing):联机事务处理
OLAP(On-Line Analytical Processing):联机分析处理
OLTP | OLAP | |
---|---|---|
主要应用 | 数据库 | 数据仓库 |
业务目的 | 侧重于业务处理,如订单、支付等业务 | 侧重于数据分析 |
主要功能 | CRUD | 查询 |
响应速度 | 实时响应 | 对时间要求不严格 |
数据库设计 | 遵循3NF | 遵循星型模式/雪花模型 |
hive和mysql主要区别
mysql | hive | |
---|---|---|
业务目的 | 侧重于业务处理,如订单、支付等 | 侧重于数据分析 |
主要功能 | CRUD | 查询 |
响应速度 | 实时响应 | 对时间要求不严格 |
数据库设计 | 遵循3NF | 遵循星型模式/雪花模型 |
处理数据规模 | 小 | 大 |
hive 和 hbase 的区别
hive 和 hbase 在大数据架构中处在不同位置,hive 主要解决数据处理和计算问题,hbase 主要解决实时数据查询问题,一般是配合使用
hive sql 和spark sql 的区别
(1)mr是基于磁盘的进行计算的,计算过程会有shuffle
(2)sql语句的解析、编译、优化、及数据的计算都由hive自身来完成
(1)spark是基于内存进行计算的
(2)sql的解析、编译、优化交由hive来处理,数据的计算却使用的是spark的rdd
sql语句的解析、编译、优化、及数据的计算都由spark自身来完成,但数据计算却不是rdd了,而是使用的是spark帮我们优化好的api( DataFrame 和 DataSet),这个api底层调用的也是rdd,但是给我spark给们进行了系统优化,开发人员直接使用api就行了
hive 运行日志 数据仓库 数据仓库显示 配置
1.hive 修改 log 存储位置
重命名hive-3.1.2/conf目录下的hive-log4j.properties.template文件名称为 hive-log4j.properties
mv hive-log4j.properties.template hive-log4j.properties
2.修改log存放位置
vim hive-log4j.properties
#向其中hive.log.dir后添加你的hive所在的路径
property.hive.log.dir = /root/bigdata/hive-3.1.2/logs
hive 数据仓库的位置配置
hive 数据仓库的原始位置是在hdfs上的:/user/hive/warehouse路径下
编辑conf目录下的hive-site.xml
<property>
<name>hive.metastore.warehouse.dir</name>
<value>/user/hive/warehouse</value>
<description>location of default database for the warehouse</description>
</property>
hive 显示数据仓库位置配置
在hive-site.xml文件中添加如下配置信息,就可以实现显示当前数据库,以及查询表的头信息配置
<property>
<name>hive.cli.print.header</name>
<value>true</value>
</property>
<property>
<name>hive.cli.print.current.db</name>
<value>true</value>
</property>
配置元数据库
1.hive内置的元数据库:Derby数据库
内嵌模式是hive metastore的默认部署模式。此种模式下,元数据存储在内置的Derby数据库,并且Derby数据库和metastore服务都嵌入在主hiveserver2进程中,即当启动hiveserver2进程时,Derby和metastore服务都会启动。缺点是一次只能支持一个客户端连接
本地模式
hive metastore服务随主hiveserver2而启动,并且运行在同一进程当中,但是存储元数据的数据库在单独的进程中运行,并且可以在单独的主机上,metastore服务将通过JDBC与元数据库进行通信
存储元数据的数据库推荐使用MySQL
hive根据hive.metastore.uris 参数值来判断,如果为空,则为本地模式
缺点是每启动一次hiveserver2,都会启动一个metastore服务
远程模式
1.远程模式下,Metastore服务在其自己的单独JVM上运行,而不随hiveserver2启动而启动,需要单独手动启动metastore服务。如果其他进程希望与Metastore服务进行通信,就需要使用Thrift Network API进行通信
2.在生产环境中,建议用远程模式来配置Hive Metastore。在这种情况下,其他依赖hive的软件都可以通过Metastore访问hive。由于还可以完全屏蔽数据库层,因此这也带来了更好的可管理性/安全性
3.远程模式下,需要配置hive.metastore.uris 参数来指定metastore服务运行的机器ip和端口
Hive复杂数据格式
hive \001格式数据 这种格式是hive默认的格式,不用指定任何的分割符
hive 多字节分隔符
- 情况一:每一行数据的分隔符是多字节分隔符,例如:”||”、“–”等
2.情况二:数据的字段中包含了分隔符
RegexSerDe正则加载(推荐)
面对情况一和情况二的问题,Hive中提供了一种特殊的方式来解决,Hive提供了一种特殊的Serde
来加载特殊数据的问题,使用正则匹配来加载数据,匹配每一列的数据
>>>Hive中默认提供了多种SerDe用于解析和加载不同类型的数据文件,常用的有ORCSerde 、RegexSerde、JsonSerDe等
分析数据,写正则表达式
原始数据格式
01||周杰伦||中国||台湾||男||七里香
写正则表达式
([0-9]*)\\|\\|(.*)\\|\\|(.*)\\|\\|(.*)\\|\\|(.*)\\|\\|(.*)
搜索一个在线网站,看看是否匹配
hive json格式数据
{"device":"device_30","deviceType":"kafka","signal":98.0,"time":1616817201390}
{"device":"device_32","deviceType":"kafka","signal":65.0,"time":1616817207131}
{"device":"device_32","deviceType":"kafka","signal":95.0,"time":1616817207714}
{"device":"device_71","deviceType":"bigdata","signal":45.0,"time":1616817207907}
create table tb_json_test2 (
device string,
deviceType string,
signal double,
`time` string
)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe' -- 指定使用专门解析json的类JsonSerDe
STORED AS TEXTFILE;
复杂json建表(array、struct)
{"common": {"ar": "230000","ba": "iPhone","ch": "Appstore","is_new": "1","md": "iPhone 8","mid": "YXfhjAYH6As2z9Iq","os": "iOS 13.2.9","uid": "485","vc": "v2.1.134"},"actions": [{"action_id": "favor_add","item": "3","item_type": "sku_id","ts": 1585744376605}],"displays": [{"displayType": "query","item": "3","item_type": "sku_id","order": 1,"pos_id": 2},{"displayType": "query ","item": "6","item_type": "sku_id","order": 5,"pos_id": 1}],"page": {"during_time": 7648,"item": "3","item_type": "sku_id","last_page_id": "login","page_id": "good_detail","sourceType": "promotion"},"err": {"error_code": "1234","msg": "***********"},"ts": 1585744374423
}
create external table ods_log_inc
(`common` struct<ar :string,ba :string,ch :string,is_new :string,md :string,mid :string,os :string,uid :string,vc:string> comment '公共信息',`page` struct<during_time :string,item :string,item_type :string,last_page_id :string,page_id:string,source_type :string> comment '页面信息',`actions` array<struct<action_id:string,item:string,item_type:string,ts:bigint>> comment '动作信息',`displays` array<struct<display_type :string,item :string,item_type :string,`order` :string,pos_id:string>> comment '曝光信息',`err` struct<error_code:bigint,msg:string> comment '错误信息',`ts` bigint comment '时间戳'
) comment '活动信息表'row format serde 'org.apache.hadoop.hive.serde2.jsonserde';
hive 事务表
Hive事务表的局限性
- 默认情况下事务配置为关闭。需要配置参数开启使用
- 仅支持ORC文件格式(STORED AS ORC)
- 表必须是分桶表(Bucketed)才可以使用事务功能
- 表参数transactional必须为true
- 外部表无法创建事务表
1.开启事务配置
set hive.support.concurrency = true; --Hive是否支持并发
set hive.enforce.bucketing = true; -- 是否开启分桶功能 从Hive2.0开始不再需要
set hive.exec.dynamic.partition.mode = nonstrict; --动态分区模式 非严格
set hive.txn.manager = org.apache.hadoop.hive.ql.lockmgr.DbTxnManager; -- 事务管理类
set hive.compactor.initiator.on = true; --是否在Metastore实例上运行启动线程和清理线程
set hive.compactor.worker.threads = 1; --在此metastore实例上运行多少个压缩程序工作线程
2.创建Hive事务表
create table trans_student(id int,name String,age int
)clustered by (id) into 2 buckets stored as orc TBLPROPERTIES('transactional'='true');
Hive 拉链表
1.什么是拉链表
保存表中的所有的数据的历史变化状态,记录数据从开始到当前的所有状态变化的信息
2.拉链表特殊的两个列
生效时间:该数据添加或者是修改的时间
失效时间:该条数据过期的时间,一般如果没有失效使用9999-12-31表示,如果失效了泽一般设置的时间就是抽取时间-1天
使用场景:表中数据量过大,部分字段发生变化 ,变化频率不高
3.闭链和开链
闭链:拉链表中某一个数据发生变化,修改这条数据的失效时间为抽取的前天
开链:修改表中的数据或是新增的数据,作为数据的最新状态;
4.拉链表实现
Oracle中:
1.先增量抽取数据。
2.将抽取到的数据和拉链表中现有的数据进行匹配(merge into),找到能够匹配到的并且是最新的那一条数据,进行闭链操作(修改失效时间)。
3.将抽取过来的数据直接追加到拉链表中,进行开链操作(insert into)。
--编写存储过程来实现拉链表
CREATE OR REPLACE PROCEDURE pro_increment_his(work_date VARCHAR2)
IS
BEGIN
--闭链操作
MERGE INTO user_t_his t USING (SELECT * FROM user_t WHERE to_char(last_date,'yyyy-mm-dd')=work_date)s
ON(t.id=s.id)
WHEN MATCHED THEN
--修改失效时间
UPDATE SET end_date=to_date(work_date,'yyyy-mm-dd')-1 WHERE t.end_date=to_date('9999-12-31','yyyy-mm-dd');
--开链
INSERT INTO user_t_his
SELECT t.*,
to_date(work_date,'yyyy-mm-dd'),
to_date('9999-12-31','yyyy-mm-dd')
FROM user_t t WHERE to_char(last_date,'yyyy-mm-dd')=work_date;
END;
1.查询所有的最新的数据
select * from 拉链表 where 失效时间=‘9999-12-31’;
2.查询某一个时间节点的切片数据
select * from 拉链表 where 生效时间<=时间节点 and 失效时间>=时间节点
Hive中:
在hive中构建拉链表使用左连接来完成拉链表的闭链操作,然后在使用union all完成开链操作,最后将处理的结果使用
insert overwrite将原来拉链表的数据进行覆盖操作
拉链表语法结构:
insert overwrite table 临时拉链表
select a.*,'抽取日期','9999-12-31' from 原表 a where 日期=抽取日期
union all
select a.列名 .....
if(b.列名 is not null and a.失效时间='9999-12-31','抽取日期-1',a.失效日期)
from 拉链表 a left join (select * from 原表 where 日期=抽取日期) b
on a.关联字段=b.关联字段
数据导入hive
1. load data local inpath '本地文件路径' [overwrite] into table 表名;
overwrite:是覆盖文件,没有overwrite是追加新的内容
hive hql查询
hive SQL查询执行顺序
from>on>join>where>Group by>聚合函数(例如分组后统计数量)>select>
Hiving>distinct>order>limit
hive count(*)&count(1)&count(字段名称)的区别
count(*)==count(1)
count(1):
统计所有行,包含nullcount(字段名称):
统计所有行,但不包含该字段下为null的行
hive union&union all 联合查询
--对两表并行查询,删除重复行,同时进行排序
select num,name from student_local
union
select num,name from student_hdfs;
--对两表并行查询,保留重复行,进行排序
select num,name from student_local
union all
select num,name from student_hdfs;
排序
order by
- 全局排序,所有数据会进入一个reducetask,容易导致内存溢出
- 强烈建议将limit与Order By一起使用。避免数据集行数过大
- 当hive.mapred.mode设置为strict严格模式时,使用不带 limit 的Order By时会引发异常
sort by
Sort By 分区排序,数据排序在Reducer前完成,数据随机被分配到各个分区,如果要指定数据去哪个分区,需要使用Distribute By
Cluster By
- 分区升序排序,分区和排序的字段一致(不能自己指定排序规则,例如降序)
- 分组的规则为hash散列:hash_func(col_name) % reduce task nums
Distribute By+Sort By
Distribute By+Sort By 类似于Cluster By,不过Distribute By负责分区,Sort By 负责区内排序,分区字段和排序字段可以不同
Distribute By:类似MR中partition,进行分区,分区规则:hash散列
Hive要求DISTRIBUTE BY语句要写在SORT BY语句之前
如果DISTRIBUTE BY +SORT BY的字段一样,并且升序排序,可以得出下列结论:
CLUSTER BY=DISTRIBUTE BY +SORT BY(字段一样)
hive 连接
1.内连接:和oracle中的内连接一样,但是hive中默认是不支持笛卡尔积和不等值连接,
如果要进行不等值连接,则必须要设置模式为:nonstrict
--设置非严格模式
set hive.mapred.mode=nostrict;
2.左半开连接:left semi join
由于早期的hive中不支持in和exists子查询,所以使用左半开连接来实现子查询的功能。
语法:select * from 表 a left semi join 表 b on a.列=b.列;
执行结果:只返回左表总符合关联条件的数据,并不会返回右表的数据。
Hive 中行列转换
行转列
方式一 : 通过 GROUP BY + CASE WHEN + 聚合函数
create table if not exists stu(uid int,cname string,score int
);insert into stu values (1,'语文',98);
insert into stu values (1,'数学',78);
insert into stu values (1,'英语',88);
insert into stu values (2,'语文',67);
insert into stu values (2,'数学',87);
insert into stu values (2,'英语',79);
insert into stu values (3,'语文',69);
insert into stu values (3,'数学',92);
insert into stu values (3,'英语',91);
insert into stu values (4,'语文',100);Select * from stu;-- 方式一 : 通过 GROUP BY + CASE WHEN + 聚合函数
select uid,sum(case when cname='语文' then score else 0 end) chinese,sum(case when cname='数学' then score else 0 end) math,sum(case when cname='英语' then score else 0 end) English
from stu group by uid;
方式二: 通过 GROUP BY + CONCAT_WS + COLLECT_LIST
CREATE TABLE IF NOT EXISTS user_order (uid BIGINT,order_id BIGINT
);insert into user_order values (1,112);
insert into user_order values (1,113);
insert into user_order values (2,114);
insert into user_order values (2,155);
insert into user_order values (3,11);
-- 方式二: 通过 GROUP BY + CONCAT_WS + COLLECT_LIST
-- 不去重 列表形式
select uid,collect_list(order_id) from user_order group by uid;
-- 去重:
select uid,collect_set(order_id) from user_order group by uid;
SELECTuid,CONCAT_WS(',', COLLECT_LIST(order_str)) AS order_list
FROM
(SELECT uid , CAST(order_id AS STRING) AS order_strFROM user_order
) tmp
GROUP BY uid
;
列转行
方式一 :采用 UNION ALL 的方式
CREATE TABLE IF NOT EXISTS explode_laterview_org(day1_num BIGINT,day2_num BIGINT,day3_num BIGINT,day4_num BIGINT,day5_num BIGINT,day6_num BIGINT,day7_num BIGINT,campaign_name STRING,campaign_id BIGINT
);INSERT OVERWRITE TABLE explode_laterview_org VALUES
(40, 20, 10, 4, 4, 2, 1, 'zoo', 2 )
,(100, 80, 53, 40, 7, 6, 5, 'moji', 3)
;
select * from explode_laterview_org;select campaign_id,campaign_name,'day1_num' type ,day1_num num from explode_laterview_org
union all
select campaign_id,campaign_name,'day2_num' type ,day2_num num from explode_laterview_org
union all
select campaign_id,campaign_name,'day3_num' type ,day3_num num from explode_laterview_org
union all
select campaign_id,campaign_name,'day4_num' type ,day4_num num from explode_laterview_org
union all
select campaign_id,campaign_name,'day5_num' type ,day5_num num from explode_laterview_org
union all
select campaign_id,campaign_name,'day6_num' type ,day6_num num from explode_laterview_org
union all
select campaign_id,campaign_name,'day7_num' type ,day7_num num from explode_laterview_org
order by campaign_id;
方式二:使用lateral view和str_to_map
SELECTcampaign_id, campaign_name, type, num
FROM explode_laterview_org
LATERAL VIEWEXPLODE(STR_TO_MAP(CONCAT('day1_num=',CAST (day1_num AS STRING),'&day2_num=',CAST (day2_num AS STRING),'&day3_num=',CAST (day3_num AS STRING),'&day4_num=',CAST (day4_num AS STRING),'&day5_num=',CAST (day5_num AS STRING),'&day6_num=',CAST (day6_num AS STRING),'&day7_num=',CAST (day7_num AS STRING)),'&', '=')) lateral_table AS type, num
;
Hive 优化
表设计
-
采用分区表,减少全表扫描
-
采用列式存储
hive 数据存储格式
hive支持的数据存储格式有TextFile(默认) SequenceFile Parquet ORC,但是尽量使用Parquet和ORC文件存储格式
- TextFile和 SequenceFile 的存储格式都是基于行存储的
- ORC 和 Parquet 是基于列式存储的
- Hive行式存储和列式存储的优缺点
(1)行式存储:
优点:数据被保存在一起了,insert和update更加容易
缺点:如果查询只涉及某几个列,它会把整行数据都读取出来,不能跳过不必要的列读取
(2)列式存储:
优点:最大的优势是查询时可以快速跳过没有涉及到的列,从而避免全表扫描;支持编码压缩;谓词下推、映射下推
缺点:insert/update会比较麻烦并且不适合扫描小量的数据
-
Parquet&ORC
- 均为二进制列式存储,均有行组、列块的概念(二进制列式存储,可以快速跳过没有涉及到的列,从而避免全表扫描)
- 均支持基本数据类型、复杂数据类型(复杂数据类型不等同嵌套数据类型)
- 均支持压缩
- 均支持谓词下推、映射下推
选择parquet原因:
- 支持嵌套数据类型(eg:json字符串),ORC不支持。 虽然ORC不支持嵌套数据类型(eg:json字符串),但可以通过复杂数据类型(eg:array和struct)把嵌套类型给表达出来
- Parquet 存储格式对Spark非常友好
选择ORC原因:
(1)比Parquet 的压缩效率高(eg:snappy)
(2)ORC存储格式对hive非常友好
(3)支持事务ACID(支持事务的表必须为分桶表)
(4)支持不同的索引索引机制(意味着orc查询速度快)
ORC为我们提供了两种索引机制:Row Group Index(行组索引) 和 Bloom Filter Index(布隆过滤器)
Row Group Index(行组索引)
在stripe(行组)记录了每个字段的最大最小值,当查询中有<,>,=的操作时,会根据最大最小值,跳过扫描不包含的stripes
Bloom Filter Index(布隆过滤器)
判读数据在不在,当它说一个值不存在的时候,一定不存在,当它说一个值存在的时候,可能存在
数据压缩
在数据规模很大和工作负载密集的情况下,采用数据压缩对磁盘I/O操作、网络数据传输有极大的帮助
hive 中的压缩就是使用了hadoop中的压缩实现的,所以hadoop中支持的压缩在hive 中都可以直接使用
hadoop中支持的压缩算法:
压缩方式选择时重点考虑:解压缩速度、压缩率、压缩后是否可以支持切片
压缩格式 | 是否支持切片 | 解压缩速度 | 压缩率 |
---|---|---|---|
snappy | no | 最快 | 很差 |
lzo | yes | 很快 | 很高 |
bzip2 | yes | 最慢 | 最高 |
gzip | no | 一般 | 很高 |
lzo压缩算法的缺点:
需要手动为文件创建索引,没有索引不支持文件切片
所有压缩算法的缺点:
加重CPU负荷,算法越复杂,解压时间越长
设置map后输出压缩
1.开启hive中间传输数据压缩功能
set hive.exec.compress.intermediate=true;2.开启mapreduce中map输出压缩功能
set mapreduce.map.output.compress=true;3.设置mapreduce中map输出数据的压缩方式
set mapreduce.map.outout.compress.codec=
org.apache.hadoop.io.compress.SnappyCodec
org.apache.hadoop.io.compress.GzipCodec
org.apache.hadoop.io.compress.BZip2Codec
org.apache.hadoop.io.compress.Lz4Codec
设置reduce后输出压缩
1.开启hive最终输出数据压缩功能
set hive.exec.compress.output=true;2.开启mapreduce最终输出数据压缩
set mapreduce.output.fileoutputformat.compress=true;3.设置mapreduce最终数据输出压缩方式
set mapreduce.output.fileoutputformat.compress.codec =
org.apache.hadoop.io.compress.SnappyCodec
org.apache.hadoop.io.compress.GzipCodec
org.apache.hadoop.io.compress.BZip2Codec
org.apache.hadoop.io.compress.Lz4Codec4.设置mapreduce最终数据输出压缩为块压缩
set mapreduce.output.fileoutputformat.compress.type=BLOCK;
mr属性优化
开启本地模式
原因:有时候数据量不大的表用本地计算模式处理,时间会很短,但是默认情况下会将任务提交到集群,等待资源分配,这个过程不仅繁琐,而且更浪费时间
限制条件:如果以下任意一个条件不满足,那么即使开启了本地模式,将依旧会提交给YARN集群运行
1.处理的数据量不超过128M
2.MapTask的个数不超过4个
3.ReduceTask的个数不超过1个
开启JVM重用
原理:JVM指代一个Java进程,假设一个mr程序中有100个MapTask,那么Hadoop默认会为每个MapTask启动一个JVM(共100个),JVM频繁的创建和销毁,会导致内存开销较大,为了解决上述问题,Hadoop中提供了JVM重用机制来,JVM重用机制可以使得一个JVM实例在同一个job中被使用N次(不同mr job中不可以),即当一个MapTask运行结束以后,JVM不会进行释放,而是继续供让一个MapTask使用,直到运行了N个以后,就会释放,N的值可以在Hadoop的mapred-site.xml文件中进行配置,通常在10-20之间,也可以再hive中直接进行设置
使用条件:适用于大量小文件场景,没有大量小文件,就不需要开启。因为开启JVM重用将一直占用使用到的task插槽,以便进行重用,直到任务完成后才能释放
-
开启Stage的并行执行
Hive会将一个查询解析为多个Stage,即阶段(例如MapReduce阶段、抽样阶段、合并阶段、limit阶段),有时候Stage彼此之间有依赖关系,只能挨个执行,但是在一些别的场景下,很多的Stage之间是没有依赖关系的,例如Union语句,Join语句等等,这些Stage没有依赖关系,但是Hive依旧默认挨个执行每个Stage,这样会导致性能非常差,我们可以通过修改参数,开启并行执行,当多个Stage之间没有依赖关系时,允许多个Stage并行执行,提高性能
-- 打开任务并行执行,默认为falseset hive.exec.parallel=true;
-- 同一个sql允许最大并行度,默认为8
set hive.exec.parallel.thread.number=16;
开启关联优化
一个Select 语句有Group by 也有 order by 时
Hive会使用两个MapReduce来完成这两个操作,第一个MapReduce做group by,经过shuffle阶段对id做分组,第二个MapReduce对第一个MapReduce的结果做order by,经过shuffle阶段对id进行排序,这样会导致性能相对较差,当我们在Hive中开启关联优化后,可以把分组和排序放在同一个MapReduce中实现
-- 默认false
set hive.optimize.correlation=true;
表之间的Join
Hive实现Join时,为了提高性能,提供了多种Join方案,例如适合小表Join大表的Map Join,大表Join大表的Reduce Join,以及大表Join的优化方案Bucket Join等
-
如果至少有一个表是小表,则尽量使用Map Join
-- 默认已经开启了Map Join
hive.auto.convert.join=true
-- hive会自动对表数据量进行判读,若到达小表条件,就将小表就加入内存
Hive会自动判断是否满足Map Join,如果不满足Map Join的条件,则自动执行Reduce Join
小表数据首会分发给每个MapTask的内存一份,然后逐次取出大表部分数据和小表进行join,底层不需要经过shuffle
-
如果2个表都是大表且频繁join,则可以选择Bucket Join
- 普通的Bucket Join
- 排序的Bucket Join,简称SMB Join(不仅分桶了,而且在桶内进行了排序)
分桶字段 = Join字段 ,桶的个数相等或者成倍数
-- 开启分桶普通join,默认false
set hive.optimize.bucketmapjoin = true;
-- 开启分桶SMB join
set hive.optimize.bucketmapjoin = true;
-- 默认true
set hive.auto.convert.sortmerge.join=true;
-- 默认false
set hive.optimize.bucketmapjoin.sortedmerge = true;
-- 默认true
set hive.auto.convert.join.noconditionaltask=true;
-
如果实在没有办法避免大表之间的join,且无法使用Bucket Join,则老老实实的使用 Reduce Join
经shuffle分组后,将key相同的数据分发到同一个reducer,实现大表之间的joinHive会自动判断是否满足Map Join,如果不满足Map Join,则自动执行Reduce Join
SQL语句
使用 from … insert 代替 union all
使用group by 代替 count(distict)
distinct的命令会在内存中构建一个hashtable,查找去重的时间复杂度是O(1),但这个关键字在使用后所有数据只会由一个Reduce Task进行处理,在大数据背景下,可能会导致Reduce Task内存溢出
group by 会分组,然后在分组内排序,如果排序方式为快速排序的话,时间复杂度是比较高O(nlogn),虽然排序花费时间长,但是不会内存溢出;另外(group by)去重会转化为两个任务,会消耗更多的磁盘网络I/O资源
-
谓词下推
on 中谓词 where 中谓词,on不仅可以作为连接条件,也可以作为过滤条件
(1)left join:右侧的表过滤条件写在on后面,左侧的表过滤条件写在where后面,性能上有提高
(2)join:写在哪里都一样
(3)full join:过滤条件写在 where 后可以谓词下推,写在 on 后不可以谓词下推(默认cbo引擎下)