在这近一个多月的学习之路上,又重新学习了一遍之前学习过的Hive,在这次回头重新学习的过程中,对Hive的使用和理解方面自然感觉比之前更加清晰。所以在学习的过程中,对之前学习内容的二次学习很有必要。
第一部分:Hive简介
(这里先不讲述Hive平台的部署以及Hive在执行中的流程,下次给大家阐述。这里主要讲解Hive的运用)
•Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能。
•本质是将SQL转换为MapReduce程序
•通用使用于离线分析
过程:Hive SQL--->Hive处理转换成Mapreduce--->HDFS
第二部分:使用Hive的优势
•操作接口采用类SQL语法,提供快速开发的能力
•使不熟悉MapReduce 的用户很方便的利用SQL 语言查询,汇总,分析数据
第三部分:Hive的运用
1.Hive建表
CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name [(col_name data_type[COMMENT col_comment],...)] [COMMENT table_comment] [PARTITIONED BY (col_name data_type(COMMENT col_comment],...)] [CLUSTERED BY (col_name,col_name,...)[SORTED BY (col_name[ASC|DESC],...)] INTO num_buckets BUCKETS] [SKEWED BY (col_name,col_name,...)] ON ((col_value,col_value,...),(col_value,col_value,...),...) [STORED AS DIRECTORIES] [ [ROW FORMAT row_format] [STORED AS file_format] | STORED BY ‘store.handler.class.name’[WITH SERDEPROPERTIES(...)]] [LOCATION hdfs_path] [TBLPROPERTIES (property_name=property_value,...)] [AS select_statement];
|
// 创建内部表
create table emp(
empno int,ename string,job string,mgr int,hiredate string,sal
double,comm double,deptno int)row format delimited fields terminated by ‘\t’;
//创建外部表
create external table emp_external(
empno int,ename string,job string,mgrint ,hiredate string,sal double,comm double,deptno int)row format delimited fields terminated by ‘\t’ location ‘/hive_external/emp/’;
//创建分区表:
CREATE TABLE order_partition(
orderNumber STRING,
Event_time STRING
)
PARTITIONED BY (event_month string)
ROW FORMAT DELIMITED FIELDS TERMINATER BY ‘\t’;
在Hive中存在两种表的类型,分别为内部表、外部表,当需要创建外部表时需要指明external,
如:create external table tbl_name
区别:当删除外部表时,表中的数据也会被删除。而内部表在被删除时,表中的数据不会被删除,数据依旧存在于HDFS中。在企业中多数使用内部表(便于多部分进行测试与开发)。
建表语句说明:
(1)CREATE TABLE创建一个指定名字的表。如果名字相同的表已经存在,则抛出异常;可以用IF NOT EXISTS选项来忽略这个异常。
(2)EXTERNAL关键字可以让用户创建一个外部表,在建表的同时指定一个指向实际数据的路径(LOCATION)。Hive创建内部表时,会将数据移动到数据仓库指向的路径;若创建外部表,仅记录数据所在的路径,不对数据的位置做任何改变。在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据。
(3)LIKE允许用户复制现有的表结构,但是不复制数据。
(4)用户在建表的时候可以自定义SerDe或者使用自带的SerDe。如果没有指定ROW FORMAT或者ROW FORMAT DELIMITED,将会使用自带的SerDe。在建表的时候,还需要为表指定列,在指定列的同时也会指定自定义的SerDe,Hive通过SerDe确定表的具体列的数据。
(5)存储格式指定:STORED AS SEQUENCEFILE|TEXTFILE|RCFILE
如果文件数据是纯文本,可以使用STORED AS TEXTFILE,也可以采用更高级的存储方式,比如ORC、Parquet等。
(6)对于每一个表(table)或者分区,Hive可以进一步组织成桶,也就是说桶是更为细粒度的数据范围划分。Hive也是针对某一列进行桶的组织。Hive采用对列值哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中。
2.Hive数据的导入
在Hive中导入数据有三种方式
①使用load语句进行导入
load:
1.从本地导入数据:load data local inpath "/opt/数据.csv" into table tbl_test;
2.从HDFS导入数据:load data inpath "/opt/数据.csv" into table tbl_test;
②直接将所需要的数据放入到Hive在hdfs中数据库存放位置 (个人习惯使用load语句进行导入)
默认情况下Hive中的表存放于HDFS中的/use/hive/warehouse/数据仓库名称/表名称。
在终端下使用put命令直接将数据存放至/use/hive/warehouse/数据仓库名称/表名称。
(使用此方式导入数据需要对数据仓库进行修复,否则将无法查询到数据。这种情况在分区表中将会具体说明。)
③Insert将查询结果插入到Hive表。
//使用insert将查询结果写入到Hive表中
Insert into table account select id,age,name from account_tmp;
//拷贝原表的指定字段
CREATE TABLE emp2 as select empno,ename,job,deptno from emp;
//使用insert将结果写入到Hive分区表
CREATE TABLE order_4_partition(
orderNumber STRING,
event_time STRING
)
ROW FORMMAT DELIMITED FIELDS TERMINATED BY ‘\t’;
//导入数据至order_4_partition
load data local inpath ‘/home/hadoop/data/order.txt’ overwrite into table order_4_partition;
//将order_4_partition表中数据迁移至分区表order_partition partition,并且指定分区要求为(event_month=’2017-07)
insert overwrite table order_partition partition(event_month=’2017-07’)select * from order_4_partition;
3、查看Hive数据库、表的相关信息。
//查看所有数据库
show databases;
//查看数据库中的表
show tables;
//查看表的所有分区信息
show partitions;
//查看Hive支持的所有函数
show functions;
//查看表的信息
desc extended t_name;
//查看更加详细的表信息
desc formatted table_name;
4.Hive中的分区,分桶操作。
(1)分区操作
Hive中的分桶分区功能,使得Hive在处理巨量数据时所执行的操作占据优势。
在我们使用分区操作时,Hive在HDFS中的数据仓库数据文件便会按照分区字段来分类创建多个不同文件夹(具体文件夹数目安排不同分区字段)
这里使用例子进行说明。
- 在Hive的分区操作中,分区分为两种:静态分区static partition和动态分区dynamic partition。静态分区和动态分区的区别在于导入数据时,是手动输入分区名称,还是通过数据来判断数据分区。对于大数据批量导入来说,显然采用动态分区更为方便。
我们需要修改Hive的默认设置以支持动态分区:
set hive.exec.dynamic.partition=true
set hive.exec.dynamic.partition.,mode=nonstrict;
sethive.exec.max.dynamic.partitions=100000;(如果自动分区数大于这个参数,将会报错) (调优参数)
set hive.exec.max.dynamic.partitions.pernode=100000;(调优参数)
- 然后使用Hive的insert命令进行插入操作。注意:需要将分区的动态字段跟在查询的最后面。
分别创建普通表tbl_phone_data_2,分区表tabl_phone_data_2
hive (default)> desc tbl_phone_data_2; OK fld_brand_name string fld_phone_name string fld_sales_time string fld_sales_channel string fld_user_score string Time taken: 0.122 seconds, Fetched: 5 row(s) hive (default)> desc tabl_phone_data_2; OK fld_phone_name string fld_sales_time string fld_sales_channel string fld_user_score string fld_brand_name string # Partition Information # col_name data_type comment fld_brand_name string Time taken: 0.17 seconds, Fetched: 10 row(s)
接下来将数据导入
hive (default)> load data inpath "/test/part*" into table tbl_phone_data_2; hive (default)> insert into table tabl_phone_data_2 partition (fld_brand_name) select fld_phone_name,fld_sales_time,fld_sales_channel,fld_user_score,fld_brand_name from tbl_phone_data_2;
- 接下来查看HDFS分区表的结构图
可见分区表tabl_phone_data_2按照tbl_brand_name进行动态分区。
(在Hive终端光标前显示当前工作数据库设置)
set hive.cli.print.current.db=true;
(2)分桶操作。
分桶作用:获得更高的查询效率
我们试想一个场景:比如2张大表需要JOIN,JOIN的字段比如是id,我们进行Reduce Side Join(ShuffleJoin)合适吗?肯定不合适。
如果我们用Map Side Join呢?Map Side Join场景是小表Join大表比较适合,因为会把小表数据是通过DistributedCache 分发到各个Map
Side,然后加载到内存和每一个Map 任务处理的大表进行JOIN,这样就不必要去做Reduce JOIN, 但是如果是大表就不太适合放到内存去了。
所以Bucket这时候在Map SideJoin就有勇武之地了。
原理:
2张表对于连接的字段进行分桶,处理左边表内某个桶的Mapper他知道右边表内对应的行在对应的桶内,因此Mapper只需要获取那个桶,然后取得数据进行JOIN
如图示:
我们需要将用户表和订单表进行join。
如果我们没有分桶,那么这2张大表在JOIN的时候,效率是很低的。
那现在我们引入桶的概念,那么用户表按照id分桶,订单表按照
cid分桶,那么相同id都会归入一个桶。那么此时再进行JOIN的时候是按照桶来JOIN的,那么大大减少了JOIN的数量。
但是这是需要一定条件的,否则JOIN出来的结果是不正确的:
用户表的桶的个数必须是订单表的倍数或者因子,也就是说订单表100各个桶,那么用户表可以是200,300或者10,20,25,50
为什么要这么做呢?
我们知道分桶会对该列进行hash,然后根据桶的数量取余计算每一个记录落在哪一个桶。
比如A表4个桶,B表5个桶,那么假设hash取余之后的数A表肯定是0,1,2,3 那么B表的话则是0,1,2,3,4
问题来呢?如果假设hashcode得到的值为8,那么在A表,这条数据就落在0这个桶内,而在表则会落在3这个桶内.
如果要想Hive执行BucketMap Join,我们需要确保这个参数是否为true:
hive.optimize.bucketmapjoin= true
step1: 先将小表做map,数据放入hashtable,广播到所有大表的Map端,大表map端接受了小表的hashtable,并不需要合并成一个大的hashtable,直接可以进行map操作,map操作会产生桶个数的Split,然后小表数据放入内存,然后大表对应的split拿出来判断。但是这时候还是有可能内存不够用,所以并没有完全解决Map Side Join在小表完全装在进内存的限制。
如果桶中的数据可以根据一个或多个列另外进行排序。由于这样对每个桶的连接变成了高效的归并排序(merge-sort), 因此可以进一步提升map端连接的效率。以下语法声明一个表使其使用排序桶:
CREATE TABLE table_name (col_name data_type,……)
CLUSTERED BY (col_name) SORTED BY(col_name ASC|DESC)
set hive.optimize.bucketmapjoin.sortedmerge = true;
sethive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHive
InputFormat;
- 方便我们抽样
我们知道抽象:
SELECT * FROM table_name TABLESAMPLE(nPERCENT);
就是针对n%进行取样
有了桶之后呢?
SELECT * FROM film TABLESAMPLE(BUCKET x OUTOF y)
x:表示从哪一个桶开始抽样
y:抽样因素,必须是桶数的因子或者倍数,假设桶数是100那么y可以是200,10,20,25,5。假设桶数是100,y=25时抽取(100/25) = 4个bucket数据;当y=200的时候(100/200) = 0.5个bucket的数据!
坚持每天中午写博客2019\2\18。