Hive再次学习<一>

1,140 阅读16分钟

其他更多java基础文章:
java基础学习(目录)


Hive基本概念

Hive 的优缺点

优点

  1. 操作接口采用类 SQL 语法,提供快速开发的能力(简单、容易上手) 。
  2. 避免了去写 MapReduce,减少开发人员的学习成本。
  3. Hive 的执行延迟比较高,因此 Hive 常用于数据分析,对实时性要求不高的场合。
  4. Hive 优势在于处理大数据,对于处理小数据没有优势,因为 Hive 的执行延迟比较 高。
  5. Hive 支持用户自定义函数,用户可以根据自己的需求来实现自己的函数。

缺点

  1. Hive 的 HQL 表达能力有限
    • 迭代式算法无法表达
    • 数据挖掘方面不擅长, 由于 MapReduce 数据处理流程的限制,效率更高的算法却无法实现。
  2. Hive 的效率比较低
    • Hive 自动生成的 MapReduce 作业,通常情况下不够智能化
    • Hive 调优比较困难,粒度较粗

Hive架构原理

image.png

用户接口: Client

CLI(command-line interface)、 JDBC/ODBC(jdbc 访问 hive)、 WEBUI(浏览器访问 hive)

元数据: Metastore

元数据包括:表名、表所属的数据库(默认是 default)、表的拥有者、列/分区字段、表的类型(是否是外部表)、表的数据所在目录等; 默认存储在自带的 derby 数据库中,推荐使用 MySQL 存储 Metastore

Hadoop/Spark

使用 HDFS 进行存储,使用 MapReduce 进行计算。

驱动器: Driver

  • 解析器(SQL Parser):将 SQL 字符串转换成抽象语法树 AST,这一步一般都用第三方工具库完成,比如 antlr;对 AST 进行语法分析,比如表是否存在、字段是否存在、 SQL语义是否有误。
  • 编译器(Physical Plan):将 AST 编译生成逻辑执行计划。
  • 优化器(Query Optimizer):对逻辑执行计划进行优化。
  • 执行器(Execution):把逻辑执行计划转换成可以运行的物理计划。对于 Hive 来说, 就是 MR/Spark。

image.png Hive 通过给用户提供的一系列交互接口,接收到用户的指令(SQL),使用自己的 Driver,结合元数据(MetaStore),将这些指令翻译成 MapReduce,提交到 Hadoop/Spark 中执行,最后,将执行返回的结果输出到用户交互接口。

Hive数据类型

基本数据类型

Hive 数据类型Java 数据类型长度例子
TINYINTbyte1byte 有符号整数20
SMALINTshort2byte 有符号整数20
INTint4byte 有符号整数20
BIGINTlong8byte 有符号整数20
BOOLEANboolean布尔类型, true 或者falseTRUE FALSE
FLOATfloat单精度浮点数3.14159
DOUBLEdouble双精度浮点数3.14159
STRINGstring字符系列。可以指定字符集。可以使用单引号或者双引号。‘now is the time ’ “for all ood men”
TIMESTAMP时间类型
BINARY字节数组

对于 Hive 的 String 类型相当于数据库的 varchar 类型,该类型是一个可变的字符串,不 过它不能声明其中最多能存储多少个字符,理论上它可以存储 2GB 的字符数。

集合数据类型

数据类型描述语法示例
STRUCT和 c 语言中的 struct 类似,都可以通过“点”符号访问元素内容。例如,如果某个列的数据类型是 STRUCT{first STRING, last STRING},那么第 1 个元素可以通过字段.first 来引用。struct() 例如: struct<street:string,city:string>
MAPMAP 是一组键-值对元组集合,使用数组表示法可以访问数据。例如,如果某个列的数据类型是 MAP,其中键->值对是’ first’ ->’ John’和’ last’ ->’ Doe’,那么可以通过字段名[‘last’ ]获取最后一个元素map() 例如: map<string, int>
ARRAY数组是一组具有相同类型和名称的变量的集合。这些变量称为数组的元素,每个数组元素都有一个编号,编号从零开始。例如,数组值为[‘John’ , ‘Doe’ ],那么第 2 个元素可以通过数组名[1]进行引用。Array() 例如 array<string>

Hive 有三种复杂数据类型 ARRAY、 MAP 和 STRUCT。 ARRAY 和 MAP 与 Java 中的 Array和 Map 类似,而 STRUCT 与 C 语言中的 Struct 类似(或者java的对象),它封装了一个命名字段集合,复杂数据类型允许任意层次的嵌套。

案例实操

  1. 假设某表有如下一行, 我们用 JSON 格式来表示其数据结构。在 Hive 下访问的格 式为
{
    "name": "songsong",
    "friends": ["bingbing" , "lili"] , //列表 Array,
    "children": {                      //键值 Map,
    "xiao song": 18 ,
    "xiaoxiao song": 19
    }
    "address": {                       //结构 Struct,
    "street": "hui long guan",
    "city": "beijing"
    }
}
  1. 基于上述数据结构, 我们在 Hive 里创建对应的表, 并导入数据。 创建本地测试文件 test.txt
songsong,bingbing_lili,xiao song:18_xiaoxiao song:19,hui long guan_beijing
yangyang,caicai_susu,xiao yang:18_xiaoxiao yang:19,chao yang_beijing

注意: MAP, STRUCT 和 ARRAY 里的元素间关系都可以用同一个字符表示, 这里用“_”。

  1. Hive 上创建测试表 test
create table test(
name string,
friends array<string>,
children map<string, int>,
address struct<street:string, city:string>
)
row format delimited fields terminated by ','
collection items terminated by '_'
map keys terminated by ':'
lines terminated by '\n';

字段解释:
row format delimited fields terminated by ',' -- 列分隔符
collection items terminated by '_' --MAP STRUCT 和 ARRAY 的分隔符(数据分割符号)
map keys terminated by ':' -- MAP 中的 key 与 value 的分隔符
lines terminated by '\n'; -- 行分隔符

  1. 导入文本数据到测试表
load data local inpath '/opt/module/hive/datas/test.txt' into table test;
  1. 访问三种集合列里的数据,以下分别是 ARRAY, MAP, STRUCT 的访问方式
hive (default)> select friends[1],children['xiao song'],address.city from test where name="songsong";
OK
_c0 _c1 city
lili 18 beijing
Time taken: 0.076 seconds, Fetched: 1 row(s)

DDL 数据定义

创建表

建表语法

CREATE [EXTERNAL] TABLE [IF NOT EXISTS] 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]
[ROW FORMAT row_format]
[STORED AS file_format]
[LOCATION hdfs_path]
[TBLPROPERTIES (property_name=property_value, ...)]
[AS select_statement]

字段解释说明

  • CREATE TABLE 创建一个指定名字的表。如果相同名字的表已经存在, 则抛出异常;用户可以用 IF NOT EXISTS 选项来忽略这个异常。
  • EXTERNAL 关键字可以让用户创建一个外部表,在建表的同时可以指定一个指向实际数据的路径(LOCATION),在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据。
  • COMMENT:为表和列添加注释。
  • PARTITIONED BY 创建分区表
  • CLUSTERED BY 创建分桶表
  • SORTED BY 不常用, 对桶中的一个或多个列另外排序
  • ROW FORMAT
ROW FORMAT DELIMITED [FIELDS TERMINATED BY char] [COLLECTION ITEMS TERMINATED BY char]  
[MAP KEYS TERMINATED BY char] [LINES TERMINATED BY char]  
| SERDE serde_name [WITH SERDEPROPERTIES (property_name=property_value,property_name=property_value, ...)]  

row format delimited fields terminated by ','
collection items terminated by '_'
map keys terminated by ':'
lines terminated by '\n';

用户在建表的时候可以自定义 SerDe 或者使用自带的 SerDe。如果没有指定 ROWFORMAT 或者 ROW FORMAT DELIMITED,将会使用自带的 SerDe。在建表的时候,用户还需要为表指定列,用户在指定表的列的同时也会指定自定义的 SerDe, Hive 通过 SerDe 确定表的具体的列的数据。
SerDe 是 Serialize/Deserilize 的简称, hive 使用 Serde 进行行对象的序列与反序列化。

  • STORED AS 指定存储文件类型 常用的存储文件类型: SEQUENCEFILE(二进制序列文件)、 TEXTFILE(文本)、 RCFILE(列式存储格式文件)。如果文件数据是纯文本,可以使用 STORED AS TEXTFILE。如果数据需要压缩,使用 STORED AS SEQUENCEFILE。
  • LOCATION :指定表在 HDFS 上的存储位置。
  • AS:后跟查询语句, 根据查询结果创建表。
  • LIKE 允许用户复制现有的表结构,但是不复制数据。

示例

(1)普通创建表

create table if not exists student(
id int, name string
)
row format delimited fields terminated by '\t'
stored as textfile
location '/user/hive/warehouse/student';

(2)根据查询结果创建表(查询的结果会添加到新创建的表中)

create table if not exists student2 as select id, name from student;

(3)根据已经存在的表结构创建表

create table if not exists student3 like student;

DML 数据操作

数据导入

向表中装载数据(Load)

语法
hive> load data [local] inpath '数据的path' [overwrite] into table
student [partition (partcol1=val1,…)];
  • load data:表示加载数据
  • local:表示从本地加载数据到 hive 表; 否则从 HDFS 加载数据到 hive 表
  • inpath:表示加载数据的路径
  • overwrite:表示覆盖表中已有数据,否则表示追加
  • into table:表示加载到哪张表
  • student:表示具体的表
  • partition:表示上传到指定分区
实操案例

(0) 创建一张表

hive (default)> create table student(id string, name string) row format delimited fields terminated by '\t';

(1) 加载本地文件到 hive

hive (default)> load data local inpath '/opt/module/hive/datas/student.txt' into table default.student;

(2) 加载 HDFS 文件到 hive 中
上传文件到 HDFS

hive (default)> dfs -put /opt/module/hive/data/student.txt /user/atguigu/hive;

加载 HDFS 上数据

hive (default)> load data inpath '/user/atguigu/hive/student.txt' into table default.student;

(3) 加载数据覆盖表中已有的数据

hive (default)> load data inpath '/user/atguigu/hive/student.txt'
overwrite into table default.student;

通过查询语句向表中插入数据(Insert)

基本插入数据
hive (default)> insert into table student_par values(1,'wangwu'),(2,'zhaoliu');
基本模式插入(根据单张表查询结果)
hive (default)> insert overwrite table student_par select id, name from student where month='201709';

insert into: 以追加数据的方式插入到表或分区, 原有数据不会删除
insert overwrite: 会覆盖表中已存在的数据
注意: insert 不支持插入部分字段

多表(多分区) 插入模式(根据多张表查询结果)
hive (default)> from student
insert overwrite table student partition(month='201707')
select id, name where month='201709'
insert overwrite table student partition(month='201706')
select id, name where month='201709';

创建表时通过 Location 指定加载数据路径

hive (default)> create external table if not exists student5(
id int, name string
)
row format delimited fields terminated by '\t'
location '/student;

数据导出

将查询的结果导出到本地

hive (default)> insert overwrite local directory '/opt/module/hive/data/export/student' select * from student;

将查询的结果格式化导出到本地

hive(default)>insert overwrite local directory '/opt/module/hive/data/export/student1'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' select * from student;

将查询的结果导出到 HDFS 上(没有 local)

hive (default)> insert overwrite directory '/user/atguigu/student2'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' select * from student;

查询

基本查询

查询语句语法:

SELECT [ALL | DISTINCT] select_expr, select_expr, ...
FROM table_reference
[WHERE where_condition]
[GROUP BY col_list]
[ORDER BY col_list]
[CLUSTER BY col_list
| [DISTRIBUTE BY col_list] [SORT BY col_list]
]
[LIMIT number]

注意:
(1) SQL 语言大小写不敏感。
(2) SQL 可以写在一行或者多行
(3)关键字不能被缩写也不能分行
(4)各子句一般要分行写。
(5)使用缩进提高语句的可读性。

Like 和 RLike

  • % 代表零个或多个字符(任意个字符)。
  • _ 代表一个字符。

RLIKE 子句
RLIKE 子句是 Hive 中这个功能的一个扩展,其可以通过 Java 的正则表达式这个更强大的语言来指定匹配条件

案例实操

(1)查找名字以 A 开头的员工信息

hive (default)> select * from emp where ename LIKE 'A%';

(2)查找名字中第二个字母为 A 的员工信息

hive (default)> select * from emp where ename LIKE '_A%';

(3)查找名字中带有 A 的员工信息

hive (default)> select * from emp where ename RLIKE '[A]';

Having 语句

having 与 where 不同点:
(1) where 后面不能写分组函数, 而 having 后面可以使用分组函数。
(2) having 只用于 group by 分组统计语句。

多表连接查询

hive (default)>SELECT e.ename, d.dname, l.loc_name
FROM emp e
JOIN dept d
ON d.deptno = e.deptno
JOIN location l
ON d.loc = l.loc;

大多数情况下, Hive 会对每对 JOIN 连接对象启动一个 MapReduce 任务。本例中会首先启动一个 MapReduce job 对表 e 和表 d 进行连接操作,然后会再启动一个 MapReduce job 将第一个 MapReduce job 的输出和表 l;进行连接操作。

注意:为什么不是表 d 和表 l 先进行连接操作呢?这是因为 Hive 总是按照从左到右的顺序执行的。
优化: 当对 3 个或者更多表进行 join 连接时,如果每个 on 子句都使用相同的连接键的话,那么只会产生一个 MapReduce job。

排序

全局排序(Order By)

Order By: 全局排序, 只有一个 Reducer

每个 Reduce 内部排序(Sort By)

Sort By: 对于大规模的数据集 order by 的效率非常低。在很多情况下, 并不需要全局排序, 此时可以使用 sort by。
Sort by 为每个 reducer 产生一个排序文件。 每个 Reducer 内部进行排序, 对全局结果集来说不是排序。

  1. 设置 reduce 个数
hive (default)> set mapreduce.job.reduces=3;
  1. 查看设置 reduce 个数
hive (default)> set mapreduce.job.reduces;
  1. 根据部门编号降序查看员工信息
hive (default)> select * from emp sort by deptno desc;
  1. 将查询结果导入到文件中(按照部门编号降序排序)
hive (default)> insert overwrite local directory '/opt/module/data/sortby-result' 
select * from emp sort by deptno desc;

分区(Distribute By)

Distribute By: 在有些情况下, 我们需要控制某个特定行应该到哪个 reducer, 通常是为了进行后续的聚集操作。 distribute by 子句可以做这件事。 distribute by 类似 MR 中 partition(自定义分区) ,进行分区,结合 sort by 使用。
对于 distribute by 进行测试,一定要分配多 reduce 进行处理,否则无法看到 distribute by 的效果。

案例实操:

先按照部门编号分区,再按照员工编号降序排序。

hive (default)> set mapreduce.job.reduces=3;

hive (default)> insert overwrite local directory
'/opt/module/data/distribute-result' select * from emp distribute by
deptno sort by empno desc;

注意:

  • distribute by 的分区规则是根据分区字段的 hash 码与 reduce 的个数进行模除后,余数相同的分到一个区。
  • Hive 要求 DISTRIBUTE BY 语句要写在 SORT BY 语句之前。

Cluster By

当 distribute by 和 sorts by 字段相同时,可以使用 cluster by 方式。
cluster by 除了具有 distribute by 的功能外还兼具 sort by 的功能。但是排序只能是升序排序, 不能指定排序规则为 ASC 或者 DESC。

以下两种写法等价:
hive (default)> select * from emp cluster by deptno;
hive (default)> select * from emp distribute by deptno sort by deptno;

分区表和分桶表

分区表

分区表实际上就是对应一个 HDFS 文件系统上的独立的文件夹,该文件夹下是该分区所有的数据文件。 Hive 中的分区就是分目录,把一个大的数据集根据业务需要分割成小的数据集。在查询时通过 WHERE 子句中的表达式选择查询所需要的指定的分区,这样的查询效率会提高很多。

分区表基本操作

创建分区表语法
hive (default)> create table dept_partition(
deptno int, dname string, loc string
)
partitioned by (day string)
row format delimited fields terminated by '\t';

注意:分区字段不能是表中已经存在的数据,可以将分区字段看作表的伪列。

加载数据
hive (default)> load data local inpath
'/opt/module/hive/datas/dept_20200401.log' into table dept_partition
partition(day='20200401');
hive (default)> load data local inpath
'/opt/module/hive/datas/dept_20200402.log' into table dept_partition
partition(day='20200402');
hive (default)> load data local inpath
'/opt/module/hive/datas/dept_20200403.log' into table dept_partition
partition(day='20200403');
查询分区表中数据
单分区查询
hive (default)> select * from dept_partition where day='20200401';
多分区联合查询
hive (default)> select * from dept_partition where day='20200401'
union
select * from dept_partition where day='20200402'
union
select * from dept_partition where day='20200403';
hive (default)> select * from dept_partition where day='20200401' or day='20200402' or day='20200403';
增加分区
创建单个分区
hive (default)> alter table dept_partition add partition(day='20200404');
同时创建多个分区
hive (default)> alter table dept_partition add partition(day='20200405')
partition(day='20200406');
删除分区
删除单个分区
hive (default)> alter table dept_partition drop partition
(day='20200406');
同时删除多个分区
hive (default)> alter table dept_partition drop partition
(day='20200404'), partition(day='20200405');
查看分区表有多少分区
hive> show partitions dept_partition;

二级分区

创建二级分区表
hive (default)> create table dept_partition2(
deptno int, dname string, loc string
)
partitioned by (day string, hour string)
row format delimited fields terminated by '\t';
正常的加载数据

(1)加载数据到二级分区表中

hive (default)> load data local inpath
'/opt/module/hive/datas/dept_20200401.log' into table
dept_partition2 partition(day='20200401', hour='12');

(2) 查询分区数据

hive (default)> select * from dept_partition2 where day='20200401' and
hour='12';

把数据直接上传到分区目录上, 让分区表和数据产生关联的三种方式

方式一:上传数据后修复

上传数据

hive (default)> dfs -mkdir -p
/user/hive/warehouse/mydb.db/dept_partition2/day=20200401/hour=13;
hive (default)> dfs -put /opt/module/datas/dept_20200401.log
/user/hive/warehouse/mydb.db/dept_partition2/day=20200401/hour=13;

查询数据(查询不到刚上传的数据)

hive (default)> select * from dept_partition2 where day='20200401' and
hour='13';

执行修复命令

hive> msck repair table dept_partition2;

再次查询数据

hive (default)> select * from dept_partition2 where day='20200401' and
hour='13';
方式二:上传数据后添加分区

上传数据

hive (default)> dfs -mkdir -p
/user/hive/warehouse/mydb.db/dept_partition2/day=20200401/hour=14;
hive (default)> dfs -put /opt/module/hive/datas/dept_20200401.log
/user/hive/warehouse/mydb.db/dept_partition2/day=20200401/hour=14;

执行添加分区

hive (default)> alter table dept_partition2 add
partition(day='201709',hour='14');

查询数据

hive (default)> select * from dept_partition2 where day='20200401' and
hour='14';
方式三: 创建文件夹后 load 数据到分区

创建目录

hive (default)> dfs -mkdir -p
/user/hive/warehouse/mydb.db/dept_partition2/day=20200401/hour=15;

上传数据

hive (default)> load data local inpath
'/opt/module/hive/datas/dept_20200401.log' into table
dept_partition2 partition(day='20200401',hour='15');

查询数据

hive (default)> select * from dept_partition2 where day='20200401' and
hour='15';

动态分区调整

关系型数据库中, 对分区表 Insert 数据时候, 数据库自动会根据分区字段的值, 将数据插入到相应的分区中, Hive 中也提供了类似的机制, 即动态分区(Dynamic Partition), 只不过,使用 Hive 的动态分区, 需要进行相应的配置。

开启动态分区参数设置

(1)开启动态分区功能(默认 true,开启)

hive.exec.dynamic.partition=true

(2)设置为非严格模式(动态分区的模式,默认 strict,表示必须指定至少一个分区为静态分区, nonstrict 模式表示允许所有的分区字段都可以使用动态分区。)

hive.exec.dynamic.partition.mode=nonstrict

(3)在所有执行 MR 的节点上,最大一共可以创建多少个动态分区。默认 1000

hive.exec.max.dynamic.partitions=1000

(4) 在每个执行 MR 的节点上,最大可以创建多少个动态分区。 该参数需要根据实际的数据来设定。比如:源数据中包含了一年的数据,即 day 字段有 365 个值,那么该参数就需要设置成大于 365,如果使用默认值 100,则会报错。

hive.exec.max.dynamic.partitions.pernode=100

(5)整个 MR Job 中,最大可以创建多少个 HDFS 文件。默认 100000

hive.exec.max.created.files=100000

(6)当有空分区生成时,是否抛出异常。一般不需要设置。默认 false

hive.error.on.empty.partition=false
案例实操

需求: 将 dept 表中的数据按照地区(loc 字段), 插入到目标表 dept_partition 的相应分区中。 (1) 创建目标分区表

hive (default)> create table dept_partition_dy(id int, name string)
partitioned by (loc int) row format delimited fields terminated by '\t';

(2) 设置动态分区

set hive.exec.dynamic.partition.mode = nonstrict;
hive (default)> insert into table dept_partition_dy partition(loc) select
deptno, dname, loc from dept;

(3) 查看目标分区表的分区情况

hive (default)> show partitions dept_partition;

分桶表以及抽样查询

Hive中的数据分桶 以及 使用场景