Apache Hive

Hive

启动准备

  1. 启动hadoop

    start-all.sh
    
  2. mysql

    service mysqld  start
    
    #建议:把mysqld服务设置为开机自启动
    chkconfig mysqld on
    
  3. 安全模式中->等待几秒->确认不是安全模式后再下一步

  4. 启动hive服务

    nohup /export/servers/hive/bin/hive --service metastore &
    
    nohup /export/servers/hive/bin/hive --service hiveserver2 &
    
  5. 客户端连接

    /export/servers/hive/bin/beeline 
    
    ! connect jdbc:hive2://node-1:10000
    root@hadoop
    

DDL-创建数据库

show databases;
create database kyletest;
use kyletest;

创建表-指定分隔符

内部表

  • 语法格式
row format [delimited | serde]

#row format :表明指定分隔符
#delimited:表明实现默认的SerDe类来进行分隔符指定  默认就是LazySimpleSerDe
#serde :表明使用其他的类进行分隔符指定
  • delimited具体语法
[fields terminated by char]   --指定字段之间的分隔符
[collection items terminated by char] --指定集合元素之间的分隔符
[map keys terminated by char]         --指定map类型 kv之间是分隔符
[lines terminated by char]            --指定换行符
  • 创建表1:
create table users(id int,name string,age int) row format delimited fields terminated by ',';	
hadoop fs -put users.txt /user/hive/warehouse/kyletest.db/users
select * from users;
+-----------+--------------+------------+----------------+--+
| users.id  | users.uname  | users.age  | users.address  |
+-----------+--------------+------------+----------------+--+
| 1         | kyle         | 18         | sz             |
| 2         | ben          | 20         | bj             |
| 3         | hana         | 21         | cs             |
| 4         | xixi         | 22         | sc             |
+-----------+--------------+------------+----------------+--+

select count(*) from users where age > 20;
巨慢
  • 创建表2:
create table t_arr(name string, works array<string>) row format delimited fields terminated by '\t' collection items terminated by ',';
select * from t_arr;
+-------------+----------------------------------------------+--+
| t_arr.name  |                 t_arr.works                  |
+-------------+----------------------------------------------+--+
| zhangsan    | ["beijing","shanghai","tianjin","hangzhou"]  |
| wangwu      | ["shanghai","chengdu","wuhan","haerbin"]     |
+-------------+----------------------------------------------+--+

select works[2] from t_arr;
+----------+--+
|   _c0    |
+----------+--+
| tianjin  |
| wuhan    |
+----------+--+

Q: 执行流程?

hive sql > MapReduce > 处理数据

Q: 表的字段类型 个数 顺序 要不要和文件保持一致?

必须一致

Q: 创建表时类型是否要保持一致?

建表的时候 hive的数据类型要和文件中类型保持一致。
如果不一致,hive会尝试转换,不保证转换成功。
如果不成功,就显示null。

Q: 结构化文件在hdfs上的位置能否任意存放?

可以

Q: 还是需要放置在指定的位置?

/user/hive/warehouse/库名.db/表名

Q: hive的数据类型?

- hive除了支持sql类型之外 ,还支持java数据类型。
- hive数据类型字面 大小写不明感。
- hive除了支持基本数据类型之外 还支持复杂类型  比如:Array  Map

-- 在使用复杂类型的时候  一定要配合分隔符的指定语法来使用。

Q: hive常见两种错误?

Error while compiling statement。 #编译期间的错误  sql语法错误
Error while processing statement. #执行期间的错误  逻辑上的错误

Q: hive的读取文件映射的原理

1. - 首先通过InputFormat类来读取文件,默认实现==TextInputFormat==。
	  一行一行的读取数据。

2. - 然后通过SerDe类根据分隔符进行一行内容切割,默认实现LazySimpleSerDe。
	SerDe == (serialization/deserialization)
  切割出来一个字段 对应表的一个字段 完成映射

Q: hive的默认分隔符?

-- 如果在建表的时候 没有指定row format。此时就会采用默认分隔符创建表。
-- 如果映射的文件中分隔符也是hive的默认分隔符,就会存在不指定分隔符也会映射成功。
-- hive的默认分隔符是一种不可见分隔符  代码\001
-- 在vim编辑器中  连续输入ctrl+V  ctrl+A

外部表

create external table … location
create external table t_flowcount(id string, phone string, mac string) row format delimited fields terminated by '\t' location '/flowCount/input';
--注意:
如果在建表的时候 有关键字external  创建的就是外部表  否则就是内部表
外部表的位置可以位于hdfs的任意路径  可以通过location 关键字指定清楚。
外部表如果不指定location 默认也是在hive默认路径下/user/hive/warehouse/库名/表名


-- 企业中 一般喜欢创建外部表 但是不指定location。
-- 内部表也称之为管理表(manage table 受hive管理的表)

Q: 内外部最大的区别(删除表的时候)?

--如果删除的是内部表
	hive表定义信息连同其映射的结构化数据文件 一起被删除。
--如果删除的是外部表
	只会删除hive中表的定义信息  结构化文件不会被删除的

Q: hive表和多个文件之间的映射问题?

一个表下面可以映射多个文件
要保证各个文件之间的结构:字段顺序、类型、个数和表定义的一致
如果不一致  能成功显示 不能成功显示null。

分区表

… partitioned by(xx xx)
-- 创建外部分区表
create external table t_user_p(id int, name string, country string) partitioned by(nationality string) row format delimited fields terminated by ',';

-- 上传数据
load data local inpath '/export/data/hivedata/china.txt' into table t_user_p partition(nationality="zh");
load data local inpath '/export/data/hivedata/usa.txt' into table t_user_p partition(nationality="mg");
select * from t_user_p;
-- 分区查询
select * from t_user_p where nationality='mg';
select * from t_user_p where nationality='zh';
+--------------+----------------+-------------------+-----------------------+--+
| t_user_p.id  | t_user_p.name  | t_user_p.country  | t_user_p.nationality  |
+--------------+----------------+-------------------+-----------------------+--+
| 1            | zhangsna       | china             | zh                    |
| 2            | lisi           | china             | zh                    |
| 3            | wangwu         | china             | zh                    |
+--------------+----------------+-------------------+-----------------------+--+

Q: 为什么需要分区表?

避免全表扫描, 可以查询优化效率,基于分区查询相当于基于文件夹查询

Q: 分区表的注意事项?

- 分区表是一种优化表 建表的时候可以不使用。

- 分区表的字段不能是表中已经存在的字段,名字不能重复。 因为分区的字段最终也会显示在查询结果上。

- 分区字段是虚拟的字段  其显示的数据内容并不位于文件中 来自于加载分区表数据的 时候指定的

- 分区表的最终表现形式就是表的文件夹下面  以分区继续创建文件夹  一个分区对应一个文件夹

- 基于不同的文件夹去管理不同分区的数据

- 使用分区字段进行过滤查询的时候 底层直接扫描文件夹即可 不需要全表扫描内容  优化了查询效率

多分区表

-- 创建内部多区表
create table t_user_multi(id int, name string, country string) partitioned by(gj string, pro string) row format delimited fields terminated by ',';

-- 上传数据
load data local inpath '/export/data/hivedata/china_bj.txt' into table t_user_multi partition(gj="zh", pro="bj");

load data local inpath '/export/data/hivedata/china_sz.txt' into table t_user_multi partition(gj='zh', pro='sz');

load data local inpath '/export/data/hivedata/usa.txt' into table t_user_multi partition(gj="mg", pro='newyork');
select * from t_user_multi where gj='zh' and pro='sz';
+---------+---------------+-------------+-----------+---------+--+
| 	 id   | 		name  		| 	country   | 		gj  	| 	pro   |
+---------+---------------+-------------+-----------+---------+--+
| 4       | lisa          | china       | zh        | sz      |
| 5       | sunyizheng    | china       | zh        | sz      |
| 6       | hanzhimin     | china       | zh        | sz      |
+---------+---------------+-------------+-----------+---------+--+

多分区表的总结

- 多分区表目前来说用的比较多的是两个分区。
- 多分区表现形式就是在前一个分区的基础上继续分区  也就是文件夹下面继续创建子文件夹
- 优化查询时候全表扫描
- 常见的分区字段
  -- 时间维度(year month  day)
  -- 地域维度(省市县)
  -- 混合维度(时间+地域)

分桶表

clustered by xxx into N buckets
-- 创建分桶表根据sno分为3个部分
create external table t_stubuck(sno int, sname string, sex string, sage int, sdept string) clustered by(sno) into 3 buckets row format delimited fields terminated by ',';

-- 开启分桶查询的功能  并且设定分桶个数  该个数要和分桶表中的个数一致
set hive.enforce.bucketing = true;
set mapreduce.job.reduces=3; --可以不设置默认会根据分桶表信息确定个数

-- 使用分桶查询 把数据从普通临时表中查询插入到最终分桶表中
insert overwrite table [t_stubuck(临时表/自己也行)]
select * from t_stubuck cluster by(Sno);

Q: 用分桶表查询的特点?

- 分桶表也是一种优化表  意味着建表的时候可以不使用
- 分桶表的字段必须是表中的字段。
- 分桶表可以去优化减少join查询的笛卡尔积。

DDL-修改表

ALTER TABLE

重命名-表

ALTER TABLE … RENAME TO …
ALTER TABLE table_name RENAME TO new_table_name;

添加列

alter table … add columns(xx xx);
ALTER TABLE t_flowcount ADD COLUMNS (count string);

删除列

alter table … replace columns();
alter table t_flowcount replace columns(id int, name string);

修改分区-表

添加分区
alter table … [add | replace] partition(xx=‘xx’) location ‘hdfs://node-21:9000/user/hive/warehouse/key=value’
-- 方式一:
alter table t_user_p add partition(nationality='rb') location '/user/hive/warehouse/kyletest.db/t_user_p/nationality=rb';

load data local inpath '/export/data/hivedata/japan.txt' into table t_user_p partition(nationality='rb');
添加多个分区
alter table t_user_p add partition(nationality='rb') location 'hdfs://node-1:8020/user/hive/warehouse/nationality=rb' partition(nationality='hg') location 'hdfs://node-1:8020/user/hive/warehouse/nationality=hg';

load data local inpath '/export/data/hivedata/korea.txt' into table t_user_p partition(nationality='hg');
删除分区
alter table … drop if exists partition(xx=‘xx’)
alter table t_user_p drop if exists partition(nationality='rb');
修改分区
alter table t_user_p partition(nationality='hg') rename to partition(nationality='hanguo');

增加属性-表

alter table … set tblproperties(xx=‘xx’);
alter table table_name set tblproperties('属性'='属性值');
desc formatted table_name;

alter table t_user_p set tblproperties('EXTERNAL'='TRUE');--外
alter table table_name set tblproperties('EXTERNAL'='FALSE');--内

修改参数

alter table … change …
alter table t_flowcount change A changeA string;

Extension

https://www.cnblogs.com/sunfie/p/4375795.html

DML-插入表

Insert [overwrite | into] + select

overwrite: 覆盖

into: 修改

多重插入数据

  • mysql
--支持语法 效率极低 底层是通过MapReduce把数据写入到hive对应的那个位置下的。
create table t_a(id int, name string);
insert into table t_a values(1, "zhangsan");

-- mysql方式-插入表, 没有保证查询的顺序!
create table t_b(name string, id int);
insert into table t_b select * from t_a;
  • hive
insert overwrite table t_b select name, id from t_a;
  • 多重插入: 一次扫描多次插入 意义在于减少全表扫描的次数。
-- 普通插入
-- ps: 本质是两个sql语句、分别进行了全表的扫描查询。
insert into table t_c select id from t_a;
insert into table t_d select name from t_a;

-- 多重插入:
-- ps: 本质是一个sql语句,完成了一次扫描 两次插入
from t_a 
insert overwrite table t_c select id 
insert overwrite table t_d select name;

-- 检查
select * from t_c inner join t_d;

动态分区

静态分区
--静态分区(手动指定分区)
create table t_user_staic (id int, name string) partitioned by(country string)  row format delimited fields terminated by ',';
load data local inpath '/root/hivedata/static.txt' into table t_user_staic partition(country="china");
动态分区
  • 在插入数据到表的时候 通过查询数据本身 根据查询的结果来确定分区值
  • 在使用动态分区之前 需要设置两个参数 一个是开启动态分区的功能 一个是指定动态分区的模式。
-- 动态分区 
-- 是否开启动态分区功能,默认false关闭。
set hive.exec.dynamic.partition=true;    

-- 动态分区的模式
-- strict(默认),表示必须指定至少一个分区为静态分区
-- nonstrict模式表示允许所有的分区字段都可以使用动态分区。
set hive.exec.dynamic.partition.mode=nonstrict;   
-- 创建
create table dynamic_partition_table(day string,ip string)row format delimited fields terminated by ",";

-- load
load data local inpath '/export/data/hivedata/date.txt' into table dynamic_partition_table;
+------------------------------+-----------------------------+--+
| dynamic_partition_table.day  | dynamic_partition_table.ip  |
+------------------------------+-----------------------------+--+
| 2015-05-10                   | ip1                         |
| 2015-05-10                   | ip2                         |
| 2015-06-14                   | ip3                         |
| 2015-06-14                   | ip4                         |
| 2015-06-15                   | ip1                         |
| 2015-06-15                   | ip2                         |
+------------------------------+-----------------------------+--+

-- 目标表
create table d_p_t(ip string) partitioned by (month string,day string);

-- 动态插入
insert overwrite table d_p_t partition (month, day) 
select ip,substr(day,1,7) as month, day from dynamic_partition_table;
+-----------------+--------------------+------------------+--+
| dynamic_par.ip  | dynamic_par.month  | dynamic_par.day  |
+-----------------+--------------------+------------------+--+
| ip1             | 2015-05            | 2015-05-10       |
| ip2             | 2015-05            | 2015-05-10       |
| ip3             | 2015-06            | 2015-06-14       |
| ip4             | 2015-06            | 2015-06-14       |
| ip1             | 2015-06            | 2015-06-15       |
| ip2             | 2015-06            | 2015-06-15       |
+-----------------+--------------------+------------------+--+

查询结果导出到文件系统

insert overwrite [local] directory … select …
  • 导出到本地
insert overwrite local directory '/root/kylechong' select * from d_p_t where month='2015-05';
├── kylechong
│   └── 000000_0
  • 导出到hdfs
insert overwrite directory '/zzz' select * from d_p_t where month="2015-06";

DML-Load

load data [local] inpath … into table …
  • load加载数据
- 含义:加载装载数据操纵  也是hive官方推荐的加载数据的方式。
- 本质:将数据文件移到到hive表所对应的hdfs的位置下。  [复制 | 移到]
- 注意:在进行数据加载的过程中 不会对数据进行任何修改。 只是单纯的数据搬运移到过程。
-- 从linux本地文件系统复制上传数据
-- 本地加载: 本质就是对文件执行复制操作 [hadoop fs -put ...]
load data local inpath '/export/data/hivedata/1.txt' into table table_name;
-- 日志显示:Loading data to table kyle.table_name from file: /export/data/hivedata/1.txt

------------------------------------------------------------

-- 将hdfs的文件移动到hive表的位置路径下
-- hdfs加载: 本质就是对文件执行了移动操作 [hadoop fs -mv ...]
load data inpath '/zzz/1.txt' into table table_name;
--日志显示;Loading data to table kyle.table_name from hdfs://node-1:8020/zzz/1.txt
  • 官方推荐
官方推荐使用load也好 自己使用hadoop fs命令也好。只要最终把数据填充到hvie表对应的那个位置目录下 ,就是成功的。当然某些操作例外,必须使用load命令。

DQL-查询表

select … from

cluster by(分且排序)

含义 根据指定的字段进行分 每个分桶内根据这个字段进行排序 默认只有正序

  • 默认: 分桶查询
select * from t_stubuck cluster by sno;

---- 
默认:  mapreduce.job.reduces=-1
INFO  : Number of reduce tasks not specified. Defaulting to jobconf value of: 1
-----
  • 设置: 分桶个数查询
set mapreduce.job.reduces=2;

select * from t_stubuck cluster by sno;
------
INFO  : Number of reduce tasks not specified. Defaulting to jobconf value of: 2
------
  • distribute by + sort by = cluster by
select * from t_stubuck distribute by sno sort by sage;

Q: 分为几个部分?

用户不设置    mapreduce.job.reduces=-1  系统自动评估决定个数。
如果用户设置  set mapreduce.job.reduces=N; N为几就是几

Q: 如何分?

如果是数值字段   字段  % 分桶个数 
如果是其他字段   字段.hashcode 5 分桶个数

Q: 分桶查询和之前分桶表之间有啥关系?

-

Q: 分桶总的 hive.enforce.bucketing参数

set hive.enforce.bucketing = true
允许根据表自动选择正确数量的reducer,并按列自动选择集群。

--官方参考资料
https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL+BucketedTables

order by [desc | asc]

-- 在编译期间reduce个数设置为1
select * from t_stubuck order by sage asc;

Q: 当手动设置reduce的个数之后 对order by全局排序是否有影响?

当sql中出现order by全局排序的时候  
意味着最终数据必须在一个文件中  
此时不管用户在环境中通过代码设置多少个reduce个数  
在编译期间 hive都会把它设置为1 保证全局。

Q: order by 和 sort by 的区别?

sort by 负责分桶内排序  先使用其他语法把数据进行分桶  sort by再针对每个分桶内进行排序
order by 负责全局排序  不会进行分桶操作 即时你设置了reduce个数  依然全局唯一输出。

inner join … on

  • 表数据
--两张表数据如下 
+-------+---------+--+
| a.id  | a.name  |
+-------+---------+--+
| 1     | a       |
| 2     | b       |
| 3     | c       |
| 4     | d       |
| 7     | y       |
| 8     | u       |
+-------+---------+--+

+-------+---------+--+
| b.id  | b.name  |
+-------+---------+--+
| 2     | bb      |
| 3     | cc      |
| 7     | yy      |
| 9     | pp      |
+-------+---------+--+
  • 内关联(只显示两边都满足join条件的结果)
select * from a inner join b on a.id=b.id;
-------等价----------
select * from a join b on a.id=b.id;

+-------+---------+-------+---------+--+
| a.id  | a.name  | b.id  | b.name  |
+-------+---------+-------+---------+--+
| 2     | b       | 2     | bb      |
| 3     | c       | 3     | cc      |
| 7     | y       | 7     | yy      |
+-------+---------+-------+---------+--+
  • 全外关联(除了不满足条件的 都显示)
select * from a full outer join b on a.id=b.id;
+-------+---------+-------+---------+--+
| a.id  | a.name  | b.id  | b.name  |
+-------+---------+-------+---------+--+
| 2     | b       | 2     | bb      |
| 4     | d       | NULL  | NULL    |
| 8     | u       | NULL  | NULL    |
| 1     | a       | NULL  | NULL    |
| 3     | c       | 3     | cc      |
| 7     | y       | 7     | yy      |
| NULL  | NULL    | 9     | pp      |
+-------+---------+-------+---------+--+
  • 左关联(以左表为准 右表与之关联 join上的显示 join不上的显示null)
select * from a left join b on a.id=b.id;
+-------+---------+-------+---------+--+
| a.id  | a.name  | b.id  | b.name  |
+-------+---------+-------+---------+--+
| 1     | a       | NULL  | NULL    |
| 2     | b       | 2     | bb      |
| 3     | c       | 3     | cc      |
| 4     | d       | NULL  | NULL    |
| 7     | y       | 7     | yy      |
| 8     | u       | NULL  | NULL    |
+-------+---------+-------+---------+--+

select * from b left join a on a.id=b.id;
+-------+---------+-------+---------+--+
| b.id  | b.name  | a.id  | a.name  |
+-------+---------+-------+---------+--+
| 2     | bb      | 2     | b       |
| 3     | cc      | 3     | c       |
| 7     | yy      | 7     | y       |
| 9     | pp      | NULL  | NULL    |
+-------+---------+-------+---------+--+
  • 右关联(以右表为准 左表与之关联 join上的显示 join不上的显示null)
select * from a right join b on a.id=b.id;
+-------+---------+-------+---------+--+
| a.id  | a.name  | b.id  | b.name  |
+-------+---------+-------+---------+--+
| 2     | b       | 2     | bb      |
| 3     | c       | 3     | cc      |
| 7     | y       | 7     | yy      |
| NULL  | NULL    | 9     | pp      |
+-------+---------+-------+---------+--+

select * from b right join a on a.id=b.id;
+-------+---------+-------+---------+--+
| b.id  | b.name  | a.id  | a.name  |
+-------+---------+-------+---------+--+
| NULL  | NULL    | 1     | a       |
| 2     | bb      | 2     | b       |
| 3     | cc      | 3     | c       |
| NULL  | NULL    | 4     | d       |
| 7     | yy      | 7     | y       |
| NULL  | NULL    | 8     | u       |
+-------+---------+-------+---------+--+

  • 内关联(只显示左表的部分)
select * from b left semi join a on a.id=b.id;
select b.* from b join a on a.id=b.id;
+-------+---------+--+
| b.id  | b.name  |
+-------+---------+--+
| 2     | bb      |
| 3     | cc      |
| 7     | yy      |
+-------+---------+--+
  • 级联问题计算(两个表笛卡尔积结果)慎用
select * from a corss join b;
本质上就是计算笛卡尔积

hive shell

/bin/hive

  • 启动服务
-- 功能1:启动hive的各种服务 
/export/servers/hive/bin/hive --service metastore    #metastore元数据服务
/export/servers/hive/bin/hive --service hiveserver2  #hiveserver2第二代服务 配合beeline使用
  • 一代客户端(可以直接连接metastore服务 访问hive)

PS: 因为有了beeline第二代客户端 bin/hive很多时间就充当启动服务的功能

/export/servers/hive/bin/hive
  • 参数

-e sql片段

-f sql脚本

-- 执行语句
/export/servers/hive/bin/hive -e 'select * from kyletest.d_p_t'

-- 执行脚本
/export/servers/hive/bin/hive -f /root/hive.sql 

-----------
-- 在线上环境中  可以使用此方式加载sql文件执行 配合调度工具进行周期重复执行。
-- 在开发环境中 程序员需要保证所写sql 代码无误 能够正确实现业务需求  提交git svn。

hive 参数的配置方式

#方式1:安装包conf/hive-site.xml

#方式2:shell 命令行的时候 启动服务 --hiveconf  k=v

#方式3:set命令设置 比如:set mapreduce.job.reduces=2;


从范围来说:依次递减
	方式1:影响此安装包任何操作  全局
	方式2:启动进程  谁设置谁启动谁生效
	方式3:会话级别  谁链接服务 谁设置 谁生效
	
从优先级上说:set命令优先级最高的  覆盖之前的设置。

在实际开发中,最多使用的是set命令设置。 对其他用户的会话没有影响 且会话结束断开链接恢复默认。
谁开发谁设置谁生效。
  • hive作为基于hadoop的数据仓库 在搭建hvie的时候指定了hadoop_home环境变量

    因此hive也会把hadoop配置文件加载解析过来作为自己的一部分

查看命令

显示所有数据库show tables;
显示当前数据库所有表show databases |schemas;
显示表分区信息,不是分区表执行报错show partitions table_name;
显示当前版本 hive 支持的所有方法show functions;
查看表信息desc extended table_name;
查看表信息(格式化美观)desc formatted table_name;
查看数据库相关信息describe database database_name;

Hive执行任务终止

'前提':hive的sql大多数都是通过MapReduce程序来执行的。因此所谓的终止指的是终止mr程序。
-- 查看当前任务列表
hadoop job -list

--+------------------------------------------------------------------------------------------+-
Total jobs:1
 JobId 
 job_1590723919346_0006       

 http://node-1:8088/proxy/application_1590723919346_0006/
 --+------------------------------------------------------------------------------------------+-
 
 -- 杀死当前job
 hadoop job -kill job_1590723919346_0006 
上一篇:Koa与Node.js开发实战(3)——Nunjucks模板在Koa中的应用(视频演示)


下一篇:iOS socket 笔记