1. Hive 初探
1.1 Hive 的数据存储
- Hive的数据存储基于Hadoop HDFS
- Hive没有专门的数据存储格式
- 存储结构主要包括:数据库、文件、表、视图、索引
- Hive默认可以直接加载文本文件(TextFile),还支持SequenceFile、RCFile
- 创建表时,指定Hive数据的列分隔符与行分隔符,Hive即可解析数据
1.2 Hive的系统架构
- 用户接口,包括 CLI,JDBC/ODBC,WebUI
- 元数据存储,通常是存储在关系数据库如 mysql, derby 中
- 解释器、编译器、优化器、执行器
- Hadoop:用 HDFS 进行存储,利用 MapReduce 进行计算
1.3 Hive的系统架构
- 用户接口主要有三个:CLI,JDBC/ODBC和 WebUI
- CLI,即Shell命令行
- JDBC/ODBC 是 Hive 的Java,与使用传统数据库JDBC的方式类似
- WebGUI是通过浏览器访问 Hive
- Hive 将元数据存储在数据库中(metastore),目前只支持 mysql、derby。Hive 中的元数据包括表的名字,表的列和分区及其属性,表的属性(是否为外部表等),表的数据所在目录等
- 解释器、编译器、优化器完成 HQL 查询语句从词法分析、语法分析、编译、优化以及查询计划(plan)的生成。生成的查询计划存储在 HDFS 中,并在随后由 MapReduce 调用执行
- Hive 的数据存储在 HDFS 中,大部分的查询由 MapReduce 完成(包含 的查询,比如 select from table 不会生成 MapRedcue 任务
1.4 Hive的metastore
- metastore是hive元数据的集中存放地。
- metastore默认使用内嵌的derby数据库作为存储引擎
- Derby引擎的缺点:一次只能打开一个会话
- 使用Mysql作为外置存储引擎,多用户同时访问
1.5 Hive 和 Hadoop 的调用关系
1、提交sql 交给驱动
2、驱动编译 解析相关的字段表信息
3、去metastore查询相关的信息 返回字段表信息
4、编译返回信息 发给驱动
5、驱动发送一个执行计划 交给执行引擎
6.1、DDLs 对数据库表的操作的, 直接和metastore交互,create table t1(name string);
6.2、完成job返回数据信息、找namenode查数据
6.3、namenode交互select count(1) from t1;
7、返回结果信息集
1.6 Hive 参数配置使用
命名空间 | 使用权限 | 描述 |
---|---|---|
hivevar | 可读写 | $ hive -d name=zhangsan; |
hiveconf | 可读写 | \$ hive –hiveconf hive.cli.print.current.db=true; $ hive –hiveconf hive.cli.print.header=true; |
system | 可读写 | java定义的配置属性,如system:user.name |
env | 只读 | shell环境变量,如env:USER |
hivevar
1
2
3
4
5
6
7# 使用场景: 起别名
hive -d name=zhangsan #传参
> create table t2(name string,${name} string); #取参数
> desc t2;
---
name string
zhangsan string
hiveconf :
1
2
3
4
5
6
7
8
9
10# 显示当前数据库名称
[ap@cs2]~% hive --hiveconf hive.cli.print.current.db=true;
hive (default)> create database mydb;
hive (default)> use mydb;
hive (mydb)>
# 显示表头(字段名)
hive --hiveconf hive.cli.print.header=true;
select * from t2;
t2.name t2.zhangsan
1.7 Hive 的脚本执行
- Hive -e “xx ”
- e 就是 edit, 在终端打印输出
- Hive -e “show tables” >> a.txt
- 可以把执行结果重定向到文件中
- Hive -S -e “show tables” >> a.txt
- -S : silence 安静的执行
- hive -f file
- hive -f hql , hql 是文件, 执行文件
- 执行完了之后, 就离开 hive 命令行
- hive -i /home/ap/hive-init.sql
- 执行完了,还在控制台, 可以继续操作
- hive>source file
- source + 文件名 : 直接执行当前目录文件
- source /home/ap/xx.sql;
1.8 hive与依赖环境的交互
- 与linux交互命令 !
!ls
!pwd
- 与hdfs交互命令
dfs -ls /
dfs -mkdir /hive
hive (default)> dfs -rm -r /user/hive/warehouse/t5;
- beeline 与 linux & hdfs 交互
- !help 查看帮助
1.9 Hive 的 JDBC 模式
JAVA API交互执行方式
hive 远程服务 (端口号1000 0) 启动方式
hive --service hiveserver2
org.apache.hive.jdbc.HiveDriver
在java代码中调用hive的JDBC建立连接
用 beeline 连接
方式1: 直接登录
- 注意: 这里的cs2是指的数据库所在的服务器, 如果mysql 安装在 cs2上, 那么不管在哪台机器上登录beeline , 都这样输入就行了
beeline -u jdbc:hive2://cs2:10000 -n ap
方式2: 输入用户名密码登录
!connect jdbc:hive2://cs2:10000
beeline注意点:
- 使用 beeline 连接时, 貌似无法与 Linux 目录交互
- 当前目录在
/home/ap/apps/apache-hive-2.3.2-bin/bin/
下 - 要传文件的话, 要使用全路径
1.10 SET命令使用
- Hive 控制台set 命令
- set; set -v; 显示所有的环境变量
set hive.cli.print.current.db=true;
set hive.cli.print.header=true;
set hive.metastore.warehouse.dir=/hive;
- hive参数初始化配置set命令:
- ~/.hiverc
- 创建此文件, 在此文件中配置初始化命令
- 补充:
hive历史操作命令集
~/.hivehistory
- ~/.hiverc
2. Hive数据类型
2.1 基本数据类型
2.2 复合数据类型
创建学生表
1 | CREATE TABLE student( |
默认分隔符 | 描述 | 语句 |
---|---|---|
\n | 分隔行 | LINES TERMINATED BY ‘\t’ |
^A | 分隔字段(列),显示编码使用\001 | FIELDS TERMINATED BY ‘\001’ |
^B | 分隔复合类型中的元素,显示编码使用\002 | COLLECTION ITEMS TERMINATED BY ‘\002’ |
^C | 分隔map元素的key和value,显示编码使用\003 | MAP KEYS TERMINATED BY ‘\003’ |
2.2.1. Struct 使用
Structs内部的数据可以通过DOT(.)来存取,例如,表中一列c的类型为STRUCT{a INT; b INT}
,我们可以通过c.a来访问域a
1 | # 数据 |
2.2.2. Array 使用
Array中的数据为相同类型,例如,假如array A中元素['a','b','c’]
,则A[1]的值为’b’
1 | # 原始数据 |
2.2.3. Map 使用
访问指定域可以通过[“指定域名称”]进行,例如,一个Map M包含了一个group->gid的kv对,gid的值可以通过M[‘group’]来获取
1 | # 原始数据 |
3. DDL , DML
3.1 DDL
3.1.1 数据库定义
默认数据库”default”
使用某个数据库
use <数据库名>
创建一个新库
1
2
3
4
5
6
7
8CREATE DATABASE
[IF NOT EXISTS] mydb
[LOCATION] '/.......'
[COMMENT] '....’;
hive>SHOW DATABASES;
hive>DESCRIBE DATABASE [extended] mydb;
hive>DROP DATABASE [IF EXISTS] mydb [CASCADE];创建
create database db1;
删除
drop database if exists db1;
- 级联删除
drop database if exists db1 cascade;
3.1.2 表定义/修改
创建表
hive>CREATE TABLE IF NOT EXISTS t1(…)
[COMMENT ‘….’]
[LOCATION ‘…’]
hive (default)> create table t4(name string,age int) row format delimited fields terminated by "\t”;
hive> SHOW TABLES in mydb;
show tables in mydb ‘’class*“
: 查看以 mydb 库中, 以 class 开头的表
hive>CREATE TABLE t2 LIKE t1; 复制表
- 只会复制表结构
hive (default)> create table t2 like t1;
hive (mydb)> create table t3 like default.employee;
- 复制其它库的表
hive>DESCRIBE t2;
- desc t2; # 效果一样的
desc extended t1;
# 查看更详细的表信息hive (default)> desc formatted t1;
# 格式化查看表的详细信息
drop table xxx;
- 删除表
查看建表语句
- show create table t_table;
修改表
- 重命名表
ALTER TABLE table_name RENAME TO new_table_name
- 增加/删除 分区
alter table student_p add partition(part='a') partition(part='b');
- 两个 partition中没有’,’
- alter table student drop partition(stat_data=‘ffff’), partition(part=‘a’),partiton(part=‘b’);
- 两个 partition中有’,’
- 增加/更新 列
ALTER TABLE table_name ADD|REPLACE COLUMNS (col_name data_type [COMMENT col_comment], ...)
- 注:ADD是代表新增一字段,字段位置在所有列后面(partition列前),REPLACE则是表示替换表中所有字段。
alter table student add columns (name1 string);
ALTER TABLE table_name CHANGE c_name new_c_name new_c_type [FIRST | AFTER c_name]
- 重命名表
3.1.3 列定义
修改列的名称、类型、位置、注释
ALTER TABLE t3 CHANGE COLUMN old_name new_name String COMMENT '...' AFTER column2;
1
2
3
4
5
6
7
8
9# 修改列名
hive (default)> alter table t1 change column name username string comment 'new name';
# 查看表
hive (default)> desc t1;
---
col_name data_type comment
username string new name
age int
---
增加列
hive> ALTER TABLE t3 ADD COLUMNS(gender int);
1
2
3
4
5
6
7
8
9
10
11
12
13
14# 查看表结构
hive (default)> desc t3;
---
col_name data_type comment
name string
---
# 添加列
hive (default)> alter table t3 add columns(gender int);
---
hive (default)> desc t3;
---
col_name data_type comment
name string
gender int
删除列 replace
- 非常不建议使用, 会造成数据错乱, 一般采取重新创建一张表的方式.
3.1.4 显示命令
show tables
show databases
show partitions
show functions
desc extended t_name;
desc formatted table_name;
3.2 DML
3.2.1 Load
语法结构
LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)]
说明:
- Load 操作只是单纯的复制/移动操作,将数据文件移动到 Hive 表对应的位置。
- filepath:
- 相对路径,例如:
project/data1
- 绝对路径,例如:
/user/hive/project/data1
- 包含模式的完整 URI,例如:
hdfs://namenode:9000/user/hive/project/data1
- 相对路径,例如:
LOCAL关键字
- 如果指定了 LOCAL, load 命令会去查找本地文件系统中的 filepath。
- 如果没有指定 LOCAL 关键字,则根据inpath中的uri查找文件
OVERWRITE 关键字
- 如果使用了 OVERWRITE 关键字,则目标表(或者分区)中的内容会被删除,然后再将 filepath 指向的文件/目录中的内容添加到表/分区中。
- 如果目标表(分区)已经有一个文件,并且文件名和 filepath 中的文件名冲突,那么现有的文件会被新文件所替代。
3.2.2 Insert
语法结构
普通插入
- INSERT OVERWRITE TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 …)] select_statement1 FROM from_statement
Multiple inserts:
1
2
3
4
5
6
7
8
9
10FROM from_statement
[INSERT OVERWRITE TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...)] select_statement1 ]
[INSERT OVERWRITE TABLE tablename2 [PARTITION ...] select_statement2] ...
# 多重插入举例
from student
insert into table student_p partition(part='a')
select * where id<95011
insert into table student_p partition(part='b')
select * where id>95011;Dynamic partition inserts:
- 不指定分区字段, 按照 from 表的分区字段插入
INSERT OVERWRITE TABLE tablename PARTITION (partcol1, partcol2 ...) select_statement FROM from_statement
导出表数据
语法结构
INSERT OVERWRITE [LOCAL] DIRECTORY directory1 SELECT ... FROM ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# 导出到本地
insert overwrite local directory '/home/ap/test/stucent1'
select * from student1;
--------------------
例子:
'查询学生信息,按性别分区,在分区内按年龄有序'
0: jdbc:hive2://cs2:10000> set mapred.reduce.tasks=2;
No rows affected (0.015 seconds)
0: jdbc:hive2://cs2:10000> insert overwrite local directory '/home/ap/ihiveout'
. . . . . . . . . . . . .> select * from student distribute by Sex sort by Sage;
--------------------
# 导出到 HDFS (仅仅是少了一个 local)
insert overwrite directory '/test/stucent1'
select * from student1;multiple inserts:
1
2
3FROM from_statement
INSERT OVERWRITE [LOCAL] DIRECTORY directory1 select_statement1
[INSERT OVERWRITE [LOCAL] DIRECTORY directory2 select_statement2] ...
4. Hive的数据模型
4.1 管理表 - 又称为内部表, 受控表
基本操作
- 创建数据文件inner_table.dat
- 创建表
hive>create table inner_table (key string);
- 加载数据
- 加载本地数据
hive>load data local inpath '/root/inner_table.dat' into table inner_table;
- 加载HDFS 上数据
hive>load data inpath ‘xxx’ into table xxx;
- 区别
- 加载 hdfs 上的数据没有 local
- 加载本地数据是 copy 一份, 加载 hdfs 上的数据是直接移动数据到加载的表目录下– mv
- 加载本地数据
- 查看数据
select * from inner_table
select count(*) from inner_table
- 删除表
drop table inner_table
- 清空表
truncate table table_name;
- 注意:
- 如果创建表的时候, 只指定了目录, 没有指定表名, 删除表的时候, 会把该目录下的所有表全部删掉
hive (mydb)> create table t2(id int)location '/home/t2';
内部表解释
- 管理表,也称作内部表,受控表
- 所有的 Table 数据(不包括 External Table)都保存在warehouse这个目录中。
- 删除表时,元数据与数据都会被删除
- 创建过程和数据加载过程(这两个过程可以在同一个语句中完成),在加载数据的过程中,实际数据会被移动到数据仓库目录中;之后对数据对访问将会直接在数据仓库目录中完成。删除表时,表中的数据和元数据将会被同时删除
内部表转为外部表, 外部表转为内部表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27hive (mydb)> create table t1(id int);
# manage_table 转换为 外部表 external_table
## 注意: 修改为外部表时, 后面2个都要大写
hive (mydb)> alter table t1 set tblproperties('EXTERNAL'='TRUE');
## 修改为内部表
hive (mydb)> alter table t1 set tblproperties('EXTERNAL'='FALSE');
# 查看t1详细信息
desc formatted t1;
---
Location: hdfs://cs1:9000/user/hive/warehouse/mydb.db/t1
Table Type: EXTERNAL_TABLE
--
# 删除t1
hive (mydb)> drop table t1;
# 此时再查看, 已经没了
hive (mydb)> show tables;
OK
tab_name
# 但是查看hdfs 路径会发现还在, 因为此表现在已经是外部表, 删除不会删除数据
dfs -ls /user/hive/warehouse/mydb.db/t1
# 如果此时再创建一个新表 t1, 表结构一样, 则数据会自动加载
4.2 ※ 外部表
4.2.1基本操作
- 创建数据文件external_table.dat
- 创建表
hive>create external table external_table1 (key string) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' location '/home/external’;
- 在HDFS创建目录/home/external
#hadoop fs -put /home/external_table.dat /home/external
- 在工作中, 一般都这样使用, 把数据上传到 hdfs 中
- 加载数据
LOAD DATA '/home/external_table1.dat' INTO TABLE external_table1;
- 查看数据
select * from external_table
select count(*) from external_table
- 删除表
drop table external_table
4.2.2 外部表解释
- 包含External 的表叫外部表
- 删除外部表只删除metastore的元数据,不删除hdfs中的表数据
- 外部表 只有一个过程,加载数据和创建表同时完成,并不会移动到数据仓库目录中,只是与外部数据建立一个链接。当删除一个 外部表 时,仅删除该链接
- 指向已经在 HDFS 中存在的数据,可以创建 Partition
- 它和 内部表 在元数据的组织上是相同的,而实际数据的存储则有较大的差异
4.2.3 外部表语法
1 | CREATE EXTERNAL TABLE page_view |
4.2.4外部表注意点:
先创建外部表/内部表, 表名为
t3
, 再往t3
传对应字段的数据, 就可以直接 select 数据了删除外部表之后, 原本数据不会删除, 此时在相同的父路径创建与被删除表字段相同&名称相同的内部/外部表, 数据也会直接加载
再看一个操作
1
2
3
4
5
6
7
8
9
10# 在 mydb.db 库下创建一个外部表 t5, 指定路径为 '/external/t5'
# 此时在 mydb.db 库的路径下是不存在 t5表的, 而是存在 '/external/t5' 中
# 但是使用 'show tables' 是存在 t5 的
hive (mydb)> create external table t5(id int) location '/external/t5';
# 往此目录传数据, 注意: 此时传过去, intdata 数据存在 t5目录下
[ap@cs2]~% hadoop fs -put intdata /external/t5
[ap@cs2]~% hadoop fs -put intdata /external/t5/i2
# 继续传数据, 查询的时候, 就是传的全部数据'相当于所有的数据都在 t5表中'
[ap@cs2]~% hadoop fs -put intdata /external/t5
# 注意: 如果传到 t5 目录下, 没有指定数据文件名的话, 会默认采用数据的名称文件.
4.3 ※ 分区表
4.3.1 基本概念和操作
- 分区可以理解为分类,通过分类把不同类型的数据放到不同的目录下。
- 分类的标准就是分区字段,可以一个,也可以多个。
- 分区表的意义在于优化查询。查询时尽量利用分区字段。如果不使用分区字段,就会全部扫描。
创建分区表, 指定分区字段
hive>CREATE TABLE t3(...) PARTITIONED BY (province string);
- 创建表的时候, 指定分区字段 key
province
为分区字段添加一个值
hive>ALTER TABLE t3 ADD [IF NOT EXISTS] PARTITION(...) LOCATION '...’;
alter table t3 add if not exists partition(province='hubei') partition(province='shanghai');
alter table t3 add if not exists partition(province='jiangsu');
- 可为此分区字段添加多个值, 为 province 添加 hubei, hunan….
查看表的分区字段&值
hive>SHOW PARTITIONS t3 [partition (province='beijing')];
删除分区
hive>ALTER TABLE t3 DROP PARTITION(province=‘beijing’.);
- 这里是删除北京的分区 (如果是内部表, 会连数据一起删除)
设置表不能被删除/查询 ——– 这里报语法错误, :TODO
- 防止分区被删除:
alter table student_p partition (part='aa') enable no_drop;
- 防止分区被查询:
alter table student_p partition (part='aa') enable offline;
- enable 和 disable 是反向操作
其它一些相关命令
SHOW TABLES; # 查看所有的表
SHOW TABLES ‘TMP‘; #支持模糊查询
SHOW PARTITIONS TMP_TABLE;
#查看表有哪些分区DESC TMP_TABLE; #查看表结构
4.3.2 创建分区表完整语法
1 | CREATE TABLE tmp_table #表名 |
4.3.3 分区表注意点(错误点)
1) 分区表在 load 数据的时候, 得指定分区, 否则会报错
1
2
3
4
5
6
7
8
9
10
11
12# 错误1: 分区表在 load 数据的时候, 得指定分区
hive (mydb)> load data local inpath '~/ihivedata/intdata' into table t6;
FAILED: SemanticException [Error 10062]: Need to specify partition columns because the destination table is partitioned
# 错误2: 导入本地数据的时候, 'path'是从当前所在路径开始的
hive (mydb)> load data local inpath '~/ihivedata/intdata' into table t6 partition(class='job1');
FAILED: SemanticException Line 1:23 Invalid path ''<sub>/ihivedata/intdata'': No files matching path file:/home/ap/</sub>/ihivedata/intdata
# 这里就正确了
hive (mydb)> load data local inpath 'ihivedata/intdata' into table t6 partition(class='job1');
Loading data to table mydb.t6 partition (class=job1)
OK本质原因:
- 分区表的分区, 就是在 hdfs 上, 原表的文件夹下面创建了一个子文件夹, 文件夹名就是分区名.
- 从本地 load 数据:
hive (mydb)> load data local inpath 'ihivedata/intdata' into table t6 partition(class='job1');
- load 数据指定分区之后, 会直接 load 到数据文件夹里面
2) 没有添加分区时, 直接往不存在的分区导入数据, 分区会自动创建
1
2# 直接往不存在的分区load数据, 分区会自动创建
load data local inpath 'ihivedata/intdata' into table t6 partition(class='job110');
3) 手动在表中创建分区(文件夹), 并直接向此文件夹中导入数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37# 直接创建目录
hive (mydb)> dfs -mkdir /user/hive/warehouse/mydb.db/t6/class=job120;
# 直接从 hadoop 端传数据
hadoop fs -put ihivedata/intdata /user/hive/warehouse/mydb.db/t6/class=job120
# 此时再 show partitions t6; 会发现并没有此分区
---
partition
class=job1
class=job110
class=job2
class=job3
class=job4
# 此时就需要手动'激活'此分区, 加入了就有了
hive (mydb)> alter table t6 add partition(class='job120');
---
hive (mydb)> show partitions t6;
---
partition
class=job1
class=job110
class=job120
class=job2
class=job3
class=job4
# 查看分区信息
hive (mydb)> select * from t6 where class='job110';
OK
t6.id t6.class
1 job110
2 job110
3 job110
4 job110
5 job110
4.3.4 复合分区
基本操作
- 创建数据文件
partition_table.dat
- 创建表
create table t7(name string,age int)partitioned by(class string,city string)row format delimited fields terminated by '\t' stored as TEXTFILE;
- 在 Hive 下加载数据到分区
load data local inpath 'ihivedata/partidata' into table t7 partition(class='job1',city='beijing');
load data local inpath 'ihivedata/partidata' into table t7 partition(class='job1',city='shanghai');
load data local inpath 'ihivedata/partidata' into table t7 partition(class='job2',city='ss');
- 注意: 多级分区其实就是多级目录
- 越靠近左边, 目录层级越高;
- 越靠近右边, 目录层级越低;
- load 数据到多级分区, load层级必须和整个层级数量相同
- 也就是说, 如果分区有2层, 传数据的时候, 也必须传2层分区, 并且层级顺序必须一致
- 从Linux 本地直接导数据到分区
- 可以直接在 hadoop UI 页面, 查看路径, 然后直接传到此路径中
hadoop fs -put ihivedata/partidata /user/hive/warehouse/mydb.db/t7/class=job1/city=beijing/p2
- 查看数据
select * from partition_table
select count(*) from partition_table
- 删除表
drop table partition_table
- 工作中 用的最多的是 外部表 + 分区表
4.4 桶表 - 主要用于抽样查询
桶表的基本操作
- 创建桶表完整过程
1 | #创建分桶表 |
保存select查询结果的几种方式:
将查询结果保存到一张新的hive表中
1
2
3create table t_tmp
as
select * from t_p;
将查询结果保存到一张已经存在的hive表中
1
2insert into table t_tmp
select * from t_p;
将查询结果保存到指定的文件目录(可以是本地,也可以是hdfs)
1
2
3
4
5insert overwrite local directory '/home/hadoop/test'
select * from t_p;
insert overwrite directory '/aaa/test'
select * from t_p;
数据加载到桶表时,会对字段取hash值,然后与桶的数量取模。把数据放到对应的文件中。
- 所以顺序是打乱的, 不是原始 t1的数据顺序
查看数据
可以直接select * 查看全部
也可以直接单独查看每个桶的数据
1
2
3
4
5
6
7
8
9hive (mydb)> dfs -cat /user/hive/warehouse/mydb.db/bucket_table/000000_0;
4
hive (mydb)> dfs -cat /user/hive/warehouse/mydb.db/bucket_table/000001_0;
5
1
hive (mydb)> dfs -cat /user/hive/warehouse/mydb.db/bucket_table/000002_0;
2
hive (mydb)> dfs -cat /user/hive/warehouse/mydb.db/bucket_table/000003_0;
3
修改桶的个数
alter table bucket_table clustered by (id) sorted by(id) into 10 buckets;
- 但是这样修改之后, 生成的是原来的 copy, 并且里面的数据也很奇怪, 不知道是按照什么来执行的? :TODO
注意:
- 物理上,每个桶就是表(或分区)目录里的一个文件
- 一个作业产生的桶(输出文件)和reduce任务个数相同
桶表工作中容易遇到的错误
向桶表中插入其它表查出的数据的时候, 必须指定字段名, 否则会报字段不匹配.
1
2
3
4FAILED: SemanticException [Error 10044]: Line 1:12 Cannot insert into target table because column number/types are different 'bucket_table': Table insclause-0 has 1 columns, but query has 2 columns.
# 应该是这样
insert into bucket_table select id from t6;
※ 桶表的抽样查询
- 桶表的抽样查询
- select * from bucket_table tablesample(bucket 1 out of 4 on id);
- tablesample是抽样语句
- 语法解析:TABLESAMPLE(BUCKET x OUT OF y)
- y必须是table总bucket数的倍数或者因子。
- hive根据y的大小,决定抽样的比例。
- 例如,table总共分了64份,当y=32时,抽取(64/32=)2个bucket的数据,当y=128时,抽取(64/128=)1/2个bucket的数据。x表示从哪个bucket开始抽取。
- 例如,table总bucket数为32,tablesample(bucket 3 out of 16),表示总共抽取(32/16=)2个bucket的数据,分别为第3个bucket和第(3+16=)19个bucket的数据。
5. Hive 视图的操作
- 使用视图可以降低查询的复杂度
- 视图的创建
- create view v1 AS select t1.name from t1;
- 视图的删除
- drop view if exists v1;
6. Hive 索引的操作
创建索引
create index t1_index on table t1(id) as 'org.apache.hadoop.hive.ql.index.compact.CompactIndexHandler' with deferred rebuild in table t1_index_table;
t1_index
: 索引名称- as: 指定索引器
t1_index_table
: 要创建的索引表
显示索引
show formatted index on t1;
重建索引
alter index t1_index on t1 rebuild;
重建完索引之后, 查看 t1_index_table 这张表, 就存了t1表文件具体的位置, 最后一列
t1_index_table._offsets
是 索引的偏移量, 类似于指针, 偏移量是索引的精髓1
2
3
4
5
6
7
8hive (mydb)> select * from t1_index_table;
OK
t1_index_table.id t1_index_table._bucketname t1_index_table._offsets
1 hdfs://cs1:9000/user/hive/warehouse/mydb.db/t1/intdata [0]
2 hdfs://cs1:9000/user/hive/warehouse/mydb.db/t1/intdata [2]
3 hdfs://cs1:9000/user/hive/warehouse/mydb.db/t1/intdata [4]
4 hdfs://cs1:9000/user/hive/warehouse/mydb.db/t1/intdata [6]
5 hdfs://cs1:9000/user/hive/warehouse/mydb.db/t1/intdata [8]
分区字段本质上其实就是索引
7. 装载数据
7.1 普通装载数据:
- 从本地 put
从 hive cp
从文件中装载数据
1
hive>LOAD DATA [LOCAL] INPATH '...' [OVERWRITE] INTO TABLE t2 [PARTITION (province='beijing')];
通过查询表装载数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# 方式1
hive>INSERT OVERWRITE TABLE t2 PARTITION (province='beijing') SELECT * FROM xxx WHERE xxx;
# 方式2
hive>FROM t4
INSERT OVERWRITE TABLE t3 PARTITION (...) SELECT ...WHERE...
INSERT OVERWRITE TABLE t3 PARTITION (...) SELECT ...WHERE...
INSERT OVERWRITE TABLE t3 PARTITION (...) SELECT ...WHERE...;
# 方式3
直接插入数据, 也会转化为文件的形式, 存在表的目录下
- insert into table_name values(xxx);
# 方式4
直接传文件
- load data (local) inpath ‘ xxx’ into table t_1;
7.2动态装载数据
不开启动态装载时
1
2hive>INSERT OVERWRITE TABLE t3 PARTITION(province='bj', city='bj')
SELECT t.province, t.city FROM temp t WHERE t.province='bj';开启动态分区支持
1
2
3hive>set hive.exec.dynamic.partition=true;
hive>set hive.exec.dynamic.partition.mode=nostrict;
hive>set hive.exec.max.dynamic.partitions.pernode=1000;把 t6 表的所有的字段 (包括分区字段) 加载进 t9 对应的分区
1
hive (mydb)> insert overwrite table t9 partition(class) select id,class from t6;
单语句建表并同时装载数据
1
hive>CREATE TABLE t4 AS SELECT ....
8. 导出数据
- 在hdfs之间复制文件(夹)
hadoop fs -cp source destination
hive> dfs -cp source destination
- 案例:
hive> dfs -get /user/hive/warehouse/mydb.db/t9 /root/t9;
- 从 hdfs 复制到本地
- 使用DIRECTORY
hive>INSERT OVERWRITE 【LOCAL】 DIRECTORY '...' SELECT ...FROM...WHERE ...;
- 案例:通过查询导出到 t9, 走的 MapReduce
- 导到到 hdfs:
insert overwrite directory "/home/t9" select * from t9;
- 导出到本地:
insert overwrite local directory "/home/ap/t9" select * from t9;
- 导到到 hdfs:
9. 读模式&写模式
- RDBMS是写模式
- Hive是读模式
10. 完整建表语句语法
1 | CREATE [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name |
11. 文件格式
- TextFile
- SequenceFile
- RCFile
- ORC
11.1 默认存储格式就是 TextFile
- 存储空间消耗比较大,
- 并且压缩的text 无法分割和合并
- 查询的效率最低,可以直接存储,
- 加载数据的速度最高
11.2 使用SequenceFile存储
- 存储空间消耗大
- 压缩的文件可以分割和合并
- 查询效率高
- 需要通过text文件转化来加载
1 | hive> |
注意点: SequenceFile 类型的表, 不能直接导入数据文件, 只能通过从他表查询
insert overwrite table t2 select * from t1;
1
2
3# 查看此 'SequenceFile' 表
hive (db2)> dfs -cat /user/hive/warehouse/db2.db/t2/000000_0 ;
SEQ"org.apache.hadoop.io.BytesWritableorg.apache.hadoop.io.Text*org.apache.hadoop.io.compress.DefaultCodec���/*<bb�m�?x�c453x�c464x�c475x�c486x�c497hive (db2)>
11.3 使用RCFile存储
RCFILE是一种行列存储相结合的存储方式。首先,其将数据按行分块,保证同一个record在一个块上,避免读一个记录需要读取多个block。其次,块数据列式存储,有利于数据压缩和快速的列存取。
- rcfile 存储空间最小
- 查询的效率最高
- 需要通过text文件转化来加载
- 加载的速度最低
1 | hive> |
注意点: RCFile 也只能从其它表导入数据
1 | hive (db2)> dfs -cat /user/hive/warehouse/db2.db/t3/000000_0; |
11.4 使用ORC存储(最好的一种格式)
是一种针对 RCFile 优化的格式
主要特点: 压缩, 索引, 单文件输出
1 | hive> |
注意点:
- ORC 也只能从其它表导入数据
- 占用空间大, 一个 block 有256M, 之前2种都是128M
1 | hive (db2)> dfs -cat /user/hive/warehouse/db2.db/t4/000000_0; |
12. 序列化 & 反序列化 (Hive SerDe)
12.1 SerDe
- What is a SerDe?
- SerDe 是 “Serializer and Deserializer.”的缩写
- Hive 使用 SerDe和FileFormat进行行内容的读写.
- Hive序列化流程
- 从 HDFS 上读入文件 (反序列化)
HDFS文件 --> InputFileFormat --> <key, value> --> Deserializer --> 行对象
- 写出到 HDFS (序列化)
行对象 --> Serializer --> <key, value> --> OutputFileFormat --> HDFS文件
- 注意: 数据全部存在在value中,key内容无意义
- 从 HDFS 上读入文件 (反序列化)
Hive 使用如下FileFormat 类读写 HDFS files:
- TextInputFormat/HiveIgnoreKeyTextOutputFormat: 读写普通HDFS文本文件.
- SequenceFileInputFormat/SequenceFileOutputFormat: 读写SequenceFile格式的HDFS文件
- ….
Hive 使用如下SerDe 类(反)序列化数据:
- MetadataTypedColumnsetSerDe: 读写csv、tsv文件和默认格式文件
- ThriftSerDe: 读写Thrift 序列化后的对象.
- DynamicSerDe: 读写Thrift序列化后的对象, 不过不需要解读schema中的ddl.
12.2 使用CSV Serde
CSV格式的文件也称为逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号。在本文中的CSV格式的数据就不是简单的逗号分割的),其文件以纯文本形式存储表格数据(数字和文本)。CSV文件由任意数目的记录组成,记录间以某种换行符分隔;每条记录由字段组成,字段间的分隔符是其它字符或字符串,最常见的是逗号或制表符。通常,所有记录都有完全相同的字段序列。
默认的分隔符是
DEFAULT_ESCAPE_CHARACTER \
DEFAULT_QUOTE_CHARACTER “ —如果没有,则不需要指定
DEFAULT_SEPARATOR ,
1 | CREATE TABLE csv_table(a string, b string) ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde' WITH SERDEPROPERTIES ("separatorChar"="\t", "quoteChar"="'", "escapeChar"="\\") STORED AS TEXTFILE; |
13. Lateral View 语法
lateral view用于和split, explode等UDTF一起使用,它能够将一行数据拆成多行数据,在此基础上可以对拆分后的数据进行聚合。lateral view首先为原始表的每行调用UDTF,UTDF会把一行拆分成一或者多行,lateral view再把结果组合,产生一个支持别名表的虚拟表。
lateral: 侧面, 横切面
Lateral View: 切面表
创建表
- create table t8(name string,nums array\<int>)row format delimited fields terminated by “\t” COLLECTION ITEMS TERMINATED BY ‘:’;
数据切割
- SELECT name,new_num FROM t8 LATERAL VIEW explode(nums) num AS new_num;
select name,id from class_test lateral view explode(student_id_list) list as id;
- 注意: as 前面的
list
貌似是可以随表起名的
效果演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22hive (default)> select * from class_test;
OK
class_test.name class_test.student_id_list
zhangsan [24,25,27,37]
lisi [28,39,23,43]
wangwu [25,23,2,54]
---
hive (default)> select name,id from class_test lateral view explode(student_id_list) list as id;
OK
name id
zhangsan 24
zhangsan 25
zhangsan 27
zhangsan 37
lisi 28
lisi 39
lisi 23
lisi 43
wangwu 25
wangwu 23
wangwu 2
wangwu 54
14. Hive的高级函数
14.1 简介
- 简单查询
- select … from…where…
- 使用各种函数
- hive>show functions;
- 展示所有函数
- hive>describe function xxx;
- 详细描述函数用法
- hive>show functions;
- LIMIT语句
- 列别名
- 嵌套select语句
14.2 高级函数分类
标准函数
- reverse()
- upper()
聚合函数
avg()
sum()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57select avg(id) from t3;
select sum(id) from t3;
# 最简答的嵌套
hive (mydb)> select t.id from (select * from t1 where id > 3)t;
# 最简单的 group by
# if else 的效果
select id,
case
when id <= 2
then 'low'
when id>=3 and id <4
then 'middle'
when id>=4 and id <5
then 'high'
else
'very high'
end
as
id_highly
from t1;
----执行结果----
id id_highly
1 low
2 low
3 middle
4 high
5 very high
--------------------
select sid,
case course
when 'yuwen' then score
else '0'
end
(别名) yuwen
case course
when 'shuxue' then score
else '0'
end
(别名) shuxue
# cast 转换函数, 大概是这么用
hive (mydb)> select id from t1 where cast(id AS FLOAT) <3.0;
OK
id
1
2- ![image-20180619150824031](http://p6i5vzkfk.bkt.clouddn.com/study/2018-06-19-070824.png)
首先当前不存在的列补0, 然后按照学号分组求和
- 然后按照 sid 分组求和/求最大值, 就可以了
- 同一列不同的放在不同的列上, 常用的方法
面试题4
array_contains()
desc function array_contains()
自定义函数
- UDF
15. Hive 性能调优
15.1 什么时候可以避免执行MapReduce?
select * or select field1,field2
limite 10
where语句中只有分区字段
使用本地
set hive.exec.mode.local.auto=true;
1
2
3
4
5select * from t3;
select name,age from t3;
select name,age from t3 limit 2;
select name,age from t3 where age=25;
# 当 where 是本地字段(列中的字段), 是不走 MR的group by语句:
- 通常和聚合函数一起使用,按照一个或者多个列对结果进行分组,然后对每组执行聚合操作
- having语句:
- 限制结果的输出
- hive将查询转化为MapReduce执行,hive的优化可以转化为mapreduce的优化!
15.2 hive是如何将查询转化为MapReduce的?-EXPLAIN的使用
- hive对sql的查询计划信息解析
EXPLAIN SELECT COUNT(1) FROM T1;
EXPLAIN EXTENDED
显示详细扩展查询计划信息
EXPLAIN EXTENDED SELECT COUNT(1) FROM T1;
为啥我的 explain extended 只有固定的几行?
- 因为这个 count 没有调动 MR, 用 sum 就会启用 MR, 会出现长长的 log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19---不会启用 MR
hive (mydb)> explain EXTENDED select count(1) from t9;
---
Explain
STAGE DEPENDENCIES:
Stage-0 is a root stage
...
-----------------------------
---这里会启用 MR
hive (mydb)> explain EXTENDED select sum(id) from t9;
---
Explain
STAGE DEPENDENCIES:
Stage-1 is a root stage
Stage-0 depends on stages: Stage-1
STAGE PLANS:
Stage: Stage-1
....
15.3 性能调优
15.3.1 本地mr
本地模式设置方式:
set mapred.job.tracker=local;
- mapper 的本地模式, 只有在开发中才会使用
set hive.exec.mode.local.auto=true;
- Hive 的执行模式
- 可以用在生产中, 因为是自动模式, 可根据参数变化
- 设置这里才会转成 local hadoop
- 按照这里设定的规则
hive.exec.mode.local.auto.input.files.max
- 测试 select 1 from wlan limit 5;
下面两个参数是local mr中常用的控制参数:
hive.exec.mode.local.auto.inputbytes.max
默认134217728- 设置local mr的最大输入数据量,当输入数据量小于这个值的时候会采用local mr的方式
hive.exec.mode.local.auto.input.files.max
默认是4设置local mr的最大输入文件个数,当输入文件个数小于这个值的时候会采用local mr的方式
大于此数时, 就不会转化为 local hadoop
1
Cannot run job locally: Number of Input Files (= 6) is larger than hive.exec.mode.local.auto.input.files.max(= 4)
可以这样修改local mr的最大输入文件个数值, 主要在调试阶段使用
1
2
3
4hive (mydb)> set hive.exec.mode.local.auto.input.files.max=8;
# 这样设置了之后, 只要文件数<=8, 就会在本地运行
Job running in-process (local Hadoop)
15.3.2 开启并行计算
- 开启并行计算,增加集群的利用率
set hive.exec.parallel=true;
15.3.3 设置严格模式
- 设置严格模式
set hive.mapred.mode=strict;
- 设置非严格模式
set hive.mapred.mode=nonstrict;
- strict可以禁止三种类型的查询:
- 强制分区表的where条件过滤
- Order by语句必须使用limit
hive (mydb)> select id from t9 where class='job110' order by id limit 3;
- 限制笛卡尔积查询
15.3.4 调整mapper和reducer的数量
- 调整mapper和reducer的数量
- 太多map导致启动产生过多开销
marpred.min.split.size
- 按照输入数据量大小确定reducer数目,
- `set mapred.reduce.tasks= 默认3
- dfs -count /分区目录/*
- hive.exec.reducers.max设置阻止资源过度消耗
- JVM重用
- 小文件多或task多的业务场景
- set mapred.job.reuse.jvm.num.task=10
- 会一直占用task槽
15.3.5 排序方面的优化
order by 语句:是全局排序, 用的比较多
1
2
3# 加个 desc 就是倒序排序
hive (mydb)> select id from bucket_table order by id desc limit 10;
Automatically selecting local only mode for querysort by 语句: 是单reduce排序
- 一般与
distribute by
结合使用
- 一般与
distribute by语句:类似于分桶,根据指定的字段将数据分到不同的 reducer,且分发算法是 hash 散列
与 sort by 结合用的比较多, 在每个分区内有序
1
2# 注意此处 distribute 的作用
hive (mydb)> select id from bucket_table distribute by id sort by id desc limit 10;
cluster by语句:
select * from t9 cluster by id;
- 可以确保类似的数据的分发到同一个reduce task中,并且保证数据有序, 防止所有的数据分发到同一个reduce上,导致整体的job时间延长
cluster by语句的等价语句:
- 如果分桶和sort字段是同一个时,此时,cluster by = distribute by + sort by
distribute by Word sort by Word ASC
15.3.6 Map-side聚合
可以直接在 .hiverc
中配置
set hive.map.aggr=true;
- 这个设置可以将顶层的聚合操作放在Map阶段执行,从而减轻清洗阶段数据传输和Reduce阶段的执行时间,提升总体性能。:TODO 不太懂
- 缺点:该设置会消耗更多的内存。
- 执行select count(1) from wlan;
15.3.6 Join 优化
- 优先过滤后再进行 Join 操作,最大限度的减少参与 join 的数据量
- 小表 join 大表,最好启动 mapjoin
- 在使用写有 Join 操作的查询语句时有一条原则:应该将条目少的表/子查询放在 Join 操作 符的左边。
- Join on 的条件相同的话,最好放入同一个 job,并且 join 表的排列顺序从小到大
在编写 Join 查询语句时,如果确定是由于 join 出现的数据倾斜,那么请做如下设置:
set hive.skewjoin.key=100000; // 这个是 join 的键对应的记录条数超过这个值则会进行
分拆,值根据具体数据量设置
set hive.optimize.skewjoin=true; // 如果是 join 过程出现倾斜应该设置为 true
16. 表连接 (只支持等值连接)
16.1 简介
INNER JOIN
- 两张表中都有,且两表符合连接条件
- select t1.name,t1.age,t9.age from t9 join t1 on t1.name=t9.name;
LEFT OUTER JOIN
- 左表中符合where条件出现,右表可以为空
- 从左表返回所有的行(字段), 右表没有匹配where 条件的话返回null
RIGHT OUTER JOIN
- 右表中符合where条件出现,左表可以为空
FULL OUTER JOIN
- 返回所有表符合where条件的所有记录,没有NULL替代
LEFT SEMI-JOIN
左表中符合右表on条件出现,右表不出现
Hive 当前没有实现 IN/EXISTS 子查询,可以用 LEFT SEMI JOIN 重写子查询语句。LEFT SEMI JOIN 的限制是, JOIN 子句中右边的表只能在 ON 子句中设置过滤条件,在 WHERE 子句、SELECT 子句或其他地方过滤都不行。
1
2
3
4
5SELECT a.key, a.value FROM a
WHERE a.key in (SELECT b.key FROM B);
# 可以被重写为:
SELECT a.key, a.val FROM a LEFT SEMI JOIN b on (a.key = b.key)
笛卡尔积
- 是m x n的结果
map-side JOIN
- 只有一张小表,在mapper的时候将小表完全放在内存中
- select /+ mapjoin(t9) /t1.name,t1.age from t9 JOIN t1on t1.name=t9.name;
16.2 代码测试
1 | 关于hive中的各种join |
17. Hive自定义函数 & Transform
17.1 自定义函数类别
- UDF
- 作用于单个数据行,产生一个数据行作为输出。(数学函数,字符串函数)
- UDAF
- (用户定义聚集函数):接收多个输入数据行,并产生一个输出数据行。(count,max)
17.2 UDF 开发实例
17.2.1 简单入门
先开发一个java类,继承UDF,并重载evaluate方法
1
2
3
4
5
6
7
8
9
10package com.rox.hive.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
public class ToLowerCase extends UDF {
// 必须是 public
public String evaluate(String field) {
String res = field.toLowerCase();
return res;
}
}打成jar包上传到服务器
将jar包添加到hive的classpath
hive>add JAR /home/ap/udf.jar;
创建临时函数与开发好的java class关联
Hive>create temporary function tolowercase as 'com.rox.hive.udf.ToLowerCase';
即可在hql中使用自定义的函数strip
select tolowercase(name) from t_1..
17.2.2 稍稍复杂
1 | # 需求: 通过一些手机号判断手机区域 |
17.2.3 有些复杂
1 | # 解析 json 数据表 rating.json |
17.3 Transform实现
1 | # 1、先加载rating.json文件到hive的一个原始表 t_json |
18. 案例
18.1 广告推送-用户画像的介绍
一个广告推送平台的项目结构示意图
18.2 累计报表实现套路(面试)
题: 求每个月的累计访问次数
此处的访问次数可以换成工资,等等..
有如下访客访问次数统计表 t_access_times
访客 | 月份 | 访问次数 |
---|---|---|
A | 2015-01 | 5 |
A | 2015-01 | 15 |
B | 2015-01 | 5 |
A | 2015-01 | 8 |
B | 2015-01 | 25 |
A | 2015-01 | 5 |
A | 2015-02 | 4 |
A | 2015-02 | 6 |
B | 2015-02 | 10 |
B | 2015-02 | 5 |
…… | …… | …… |
需要输出报表:t_access_times_accumulate
访客 | 月份 | 月访问总计 | 累计访问总计 |
---|---|---|---|
A | 2015-01 | 33 | 33 |
A | 2015-02 | 10 | 43 |
……. | ……. | ……. | ……. |
B | 2015-01 | 30 | 30 |
B | 2015-02 | 15 | 45 |
……. | ……. | ……. | ……. |
解题代码
1 |
|
18.3 待做项目
19.注意点
- 使用聚合函数, 后面一定要分组(group by xxx), group by 会自动去重
- 如果 sql 语句中有 group by, 那么 select 后面必须有 group by 的字段, 或聚合函数
- 使用order by 排序某个字段时, 必须在 select中出现此字段, 否则会找不到 :TODO
- 待验证