大数据-hive入门详解

目录

      • hive概述
        • 什么是hive?
        • HQL转换成MR流程
        • hive的优缺点
        • hive的架构
        • hive和数据库
        • hive数据类型
        • hive SQL
        • hive 常用函数
        • hive 文件存储

hive概述

什么是hive?

  • 维基百科-hive
  • Apache Hive 中文手册
  • hive是建立在hadoop架构上的数据仓库工具,能够提供数据精炼、查询和分析,能够将结构化的数据文件映射成一张表,并提供类SQL查询功能,底层是将SQL转换成MR程序

HQL转换成MR流程

  • 首先hive通过对SQL进行统计分析,将SQL语言中常用的操作用MapReduce写成很多模板,所有的MapReduce模板都封装在hive中
  • 用户根据业务逻辑编写HQL语句
  • 通过hive框架匹配出响应的MapReduce模板
  • 运行MapReduce程序,生成相应的结果返回给客户端

hive的优缺点

  • 优点
  1. 操作接口采用类SQL语法,提供快速开发能力(简单易上手)
  2. 避免了写MapReduce程序,减少了开发人员的学习成本
  3. hive适合处理大数据
  4. 支持用户自定义函数,用户可以根据自己的需求实现自己的函数
  • 缺点
  1. hive的SQL表达能力有限,迭代式算法无法表达,不擅长数据挖掘
  2. hive的执行延时较高,适合实时性不高的场合,处理小数据没有优势
  3. hive自动生成的MapReduce通常情况下不够智能,调优比较困难,粒度较粗

hive的架构

大数据-hive入门详解_第1张图片

  • 用户接口 client
    CLI(hive shell) JDBC WEBUI
  • 元数据 metastore
    元数据包括:表名 ,表所属的数据库,表的拥有者,列/分区字段,表的类型,表的数据所在目录等,默认存放在自带的Derby数据库中,推荐使用MySQL存储metastore
  • hadoop
    使用HDFS进行存储数据,使用MapReduce进行计算
  • 驱动器 Driver
    ① 解析器(SQL parser): 将SQL字符串转换成抽象语法树AST,一般用第三方工具库完成,比如antlr;对AST进行语法分析,比如表是否存在,字段是否存在,SQL语义是否有误
    ② 编译器(Physical Plan): 将AST编译生成逻辑执行计划
    ③ 优化器(Query Optimizer): 对逻辑执行计划进行优化
    ④ 执行器(Execution): 把逻辑执行计划转换成可以运行的物理计划,对于hive来说就是MR

hive和数据库

hive使用了类似SQL的查询语言HQL,因此容易将hive理解为数据库.其实hive和数据库除了有类似的查询语句之外,并没有其他地方类似.

  • hive和数据库语言类似
  • hive是建立在hadoop之上的,所有hive的数据都存储在HDFS中,而数据库则可以将数据保存在块设备或者本地文件系统之中
  • hive是针对数据仓库应用设计的,因此不建议对数据进行改写,所有的数据都是在加载的时候确定好的,而数据库中的数据经常涉及到修改操作
  • hive在访问数据中满足条件的特定值时,需要暴力扫描整个数据,因此访问延迟较高,但是由于MapReduce的引入,hive可以并行访问数据,因此没有索引,对于大数据量的访问,hive仍然很有优势.数据库中,通常会针对一个或几个列建立索引,因此对于少量的特定条件的数据的访问,数据库可以有很高的效率,较低的延迟.
  • hive中大多数查询的执行是通过hadoop提供的MapReduce来实现的,而数据库通常有自己的执行引擎
  • 由于MapReduce框架本身具有较高的延迟,所以在利用MapReduce执行hive查询的时候,也会有较高的延迟.数据库在数据规模较小的时候,执行延迟较低,数据规模大的时候,hive的并行计算是很有优势的
  • hive是建立在hadoop上的,所以hive的可扩展性和hadoop的可扩展性是一致的.数据库由于ACID语义的严格控制,扩展性有限
  • hive支持很大规模的数据,数据库支持的数据规模较小

hive数据类型

  • 基本数据类型
hive数据类型			java数据类型		长度						例子
tinyint				byte			1byte有符号整数			1
smalint				short			2byte有符号整数			2
int					int				4byte有符号整数			3
bigint				long			8byte有符号整数			4
boolean				boolean			布尔类型,truefalse		
float				float			单精度浮点数				2.1
double				double			双精度浮点数				2.2
string				string			字符串					'aaa' "bbb"
timestamp							时间类型		
binary								字节数组

hive的原子数据类型是可以进行隐式转换的,类似于java的类型转换,但是不会反向转换,除非使用cast
  • 集合数据类型
struct类型
	struct{value1 string,value2 int}
map类型
	键值对元组集合,键值对是 key1->value1
array类型
	数组是一组具有相同类型和名称的变量集合 [value1, value2]

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 struct array 的分隔符]
map keys terminated by ':'  [map中key value的分隔符]
lines terminated by '\n';   [行分隔符]

访问形式:  friends[1]   children['key']   address.city

hive SQL

创建数据库: create database if not exists  db_name;
		  create database db_name location '/db_aname.db';--指定在hdfs上的路径
查询数据库: show databases;
		  show databases like 'order_*';
数据库信息:  desc database db_name;
		   desc database  extended db_name;
切换数据库: use db_name1;
修改数据库部分参数: alter database db_name set dbproperties('key'='value');
<-(*****!!!慎用!!!****)
删除数据库: drop database db_name; drop database if exists db_name;
		 如果数据库不为空,使用cascade: drop database db_name cascade;
(*****!!!慎用!!!****)->	  
建表语句: 
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]
解释:
create table 创建表,使用if not exists 规避表存在异常
external 创建外部表时使用,和内部表的区别在于,删除外部表时只删除元数据,不删除数据,内部表删除的时候元数据和数据一起被删除
comment 做注释说明
partitioned by 创建分区表,后面跟分区字段名称和类型
clustered by  创建分通表
sorted by 桶内排序,指定排序字段

(1)row format delimited 
[fields terminated by char] 
[collection items terminated by char]
[map keys terminated by char] 
[lines terminated by char](2)serde serde_name [with serdeproperties (property_name=property_value, property_name=property_value, ...)]
用户在建表的时候可以自定义serde或者使用自带的serde,如果没有指定(1),将会使用自带的serde.serde是serialize/deserilize 的简称,目的是用于序列化和反序列化.
fields terminated by char : 数据列分隔符
collection items terminated by char: 集合元素分隔符
map keys terminated by char : map分隔符
lines terminated by char: 数据行分隔符

stored as 指定存储文件类型,常用的数据存储文件类型: sequencefile(二进制序列文件),textfile(文本),rcfile(列式存储格式文件)
location: 指定表在hdfs上的存储位置
like: 允许用户复制现有的表结构,但是不复制数据

例1: 
create table if not exists table1(
 id int comment "id",
 name string comment "名称"
 ) row format delimited fields terminated by '\t';2:
create table if not exists table2 as select id,name from table1;3:
create table if not exists table3 like table2;

外部表和内部表(管理表)转换:
内部表=>外部表: alter table table1 set tblproperties('EXTERNAL'='TRUE');
外部表=>内部表: alter table table1 set tblproperties('EXTERNAL'='FALSE');

查看表结构: desc formatted table1
分区表:
create table if not exists table1(
 id int comment "id",
 name string comment "名称")
 partitioned by (mon string)
row format delimited fields terminated by '\t';

LOAD DATA LOCAL INPATH '/path/to/local/files'
[OVERWRITE|INTO] TABLE test
PARTITION (country='CHINA')

加载数据: 如果是本地的数据,local,文件会被拷贝到hdfs上,如果是hdfs的文件,则会直接移动文件.
		into 是直接追加数据,overwrite是覆盖数据

增加分区: alter table table1 add partition(mon='202009')
增加多个分区: alter table table1 add partition(mon='202010') partition('202007')
删除分区: alter table table1 drop partition(mon='202008')
查看分区: show partitions table1
更新列: ALTER TABLE table_name 
       CHANGE [COLUMN] col_old_name col_new_name column_type 
       [COMMENT col_comment] [FIRST|AFTER column_name] 
例: alter table test change a a1 int after b; 将a列名称更新为a1,类型替换为int,位置放在b列之后

增加和替换列:
ALTER TABLE table_name 
ADD|REPLACE COLUMNS (col_name data_type [COMMENT col_comment], ...) 

例: alter table test add columns(a1 int,b1 string); 添加a1和b1 两列在列末尾,分区列之前.
replace 则是替换表中所有的字段

插入数据: insert [into|overwrite] table test partition(par='xxx') values(xx,xx,xx);
 
 insert [into|overwrite] table test partition(par='xxx') select xx,xx,xx from test2;
 
 from  table1
 insert overwrite table test partition(par='xxx') select xx,xx where xx
 insert into table test partition(par='xxx') select xx,xx where xx
数据导出:
insert overwrite local directory '/path/xxx.txt' 
row format delimited fields terminated by '\t'
select * from table_name;local则是本地路径,没有local就是hdfs路径

清除表数据: truncate table test; 
truncate只能删除管理表,不能删除外部表数据.
查询语句,没什么说的
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]

group by 之后可以跟 having 对结果进行筛选,可以使用别名.
where 是针对表数据进行过滤,之后不能写聚合函数,having 可以

order by 全局排序,默认升序asc

distribute by 分区,结合sort by 使用,分区排序,写在sort by 之前

cluster by 当分区distribute by 的字段和sort by 的字段相同时,使用cluster by,但是只能是升序

hive 常用函数

查看所有函数: show functions;
查看函数用法: desc function func_name;  desc function extended func_name;(带例子)
  • 行转列 & 列转行
nvl(value,default_value) - Returns default value if value is null else returns value
判断value是否是null,如果是的话给个默认值,不是的话返回value本身
 
date_format('2020-08-08','yyyyMMdd'): 日期格式化 20200808

date_add('2020-08-08',1): 日期加12020-08-09
date_sub('2020-08-08',1): 日期减12020-08-07
将第二个参数设置为负数就可以起到另一个的作用

case when: 将sal低于1000的都加500,不符合条件的还是sal
select 
	id,(case when sal<1000 then sal+500 else sal end) as sal
from
	emp;

行转列:
concat(str1,str2,str3....): 返回字符串拼接后的结果,有任一个参数为null,则返回null
concat_ws(分隔符,str1,str2,str3....): 第一个参数为分隔符,将字符串按照指定分隔符拼接,会跳过分隔符之后的任何null和空字符串
collect_set(col): 函数只接受基本数据类型,它的主要作用是将某字段的值进行去重汇总,产生array类型


列转行:
explode(col): 将hive一列中复杂的array或者map拆分成多行
lateral view : LATERAL VIEW udtf(expression) tableAlias AS columnAlias,和udtf函数结合使用,将一列数据拆成多行,在此基础上可以对拆分后的数据进行聚合.

美团面试SQL:
from t_table
原始数据
label					result
1001,1002,1003			1001:1,1002:3,1003:1,1004:1
需要数据
label   result
1001	1
1003	1
找出label中元素result为1select
    label1,result1
from
    t_table as t1
lateral view explode(str_to_map(t1.result)) tmp as label1,result1
where result1=1  and  instr(t1.label,label1)>0

str_to_map(text, delimiter1, delimiter2): 字面意思,将字符串转换成map,第一个分隔符是kv对和kv对之间的分隔符,默认',',第二个是kv之间的分隔符,默认是':'.
instr(str, substr) - 返回substr在str中第一次出现的位置,没有返回0
过程分解:
with  t1  as
(select '1001,1002,1003' as pm1,'1001:1,1002:2,1003:1,1004:1' as pm2)
select  mk,mv
from  t1
lateral view explode(str_to_map(pm2)) tm2 as mk,mv
将'1001:1,1002:3,1003:1,1004:1' 爆炸开
mk		mv
1001	1
1002	2
1003	1
1004	1
然后对结果 where 筛选
where mv=1  and  instr(t1.pm1,mk)>0
  • 窗口函数
over(): 指定分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化

current row: 当前行
n preceding: 往前n行数据
n following: 往后n行数据
unbounded: 起点, unbounded preceding 表示从前面的起点,unbounded following表示到后面的终点
lag(col,n): 往前第 n 行数据
lead(col,n): 往后第 n 行数据
ntile(n): 把有序分区中的行分发到指定数据的组中,各个组有编号,编号从1开始,对于每一行,ntile返回此行所属的组的编号

例子: 
name orderdate	cost
jack,2017-01-01,10
tony,2017-01-02,15
jack,2017-02-03,23
tony,2017-01-04,29
jack,2017-01-05,46
jack,2017-04-06,42
tony,2017-01-07,50
jack,2017-01-08,55
mart,2017-04-08,62
mart,2017-04-09,68
neil,2017-05-10,12
mart,2017-04-11,75
neil,2017-06-12,80
mart,2017-04-13,94

 (1)查询174月份购买过的顾客及总人数
  select 
  	name,count(*) over()
  from
  	test
  where
  	substring(orderdate,1,7) = '2017-04'
  group by
  	name;
  
 jack	2
 mart	22)查询顾客的购买明细及月购买总额
  select
  	name,orderdate,cost,sum(cost) over(partition by month(orderdate))
  from
  	test;
jack	2017-01-01	10	205
tony	2017-01-02	15	205
tony	2017-01-04	29	205
jack	2017-01-05	46	205
tony	2017-01-07	50	205
jack	2017-01-08	55	205
jack	2017-02-03	23	23
jack	2017-04-06	42	341
mart	2017-04-08	62	341
mart	2017-04-09	68	341
mart	2017-04-11	75	341
mart	2017-04-13	94	341
neil	2017-05-10	12	12
neil	2017-06-12	80	803)上述的场景,要将cost按照日期进行累加
select name,orderdate,cost,
sum(cost) over() as sample1,--所有行相加
sum(cost) over(partition by name) as sample2,--按name分组,组内数据相加
sum(cost) over(partition by name order by orderdate) as sample3,--按name分组,组内数据累加
sum(cost) over(partition by name order by orderdate rows between UNBOUNDED PRECEDING and current row ) as sample4 ,--和sample3一样,由起点到当前行的聚合
sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING and current row) as sample5, --当前行和前面一行做聚合 
sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING AND 1 FOLLOWING ) as sample6,--当前行和前边一行及后面一行
sum(cost) over(partition by name order by orderdate rows between current row and UNBOUNDED FOLLOWING ) as sample7 --当前行及后面所有行
from test;

tony	2017-01-02	15	661	94	15	15	15	44	94
tony	2017-01-04	29	661	94	44	44	44	94	79
tony	2017-01-07	50	661	94	94	94	79	79	50
jack	2017-01-01	10	661	176	10	10	10	56	176
jack	2017-01-05	46	661	176	56	56	56	111	166
jack	2017-01-08	55	661	176	111	111	101	124	120
jack	2017-02-03	23	661	176	134	134	78	120	65
jack	2017-04-06	42	661	176	176	176	65	65	42
neil	2017-05-10	12	661	92	12	12	12	92	92
neil	2017-06-12	80	661	92	92	92	92	92	80
mart	2017-04-08	62	661	299	62	62	62	130	299
mart	2017-04-09	68	661	299	130	130	130	205	237
mart	2017-04-11	75	661	299	205	205	143	237	169
mart	2017-04-13	94	661	299	299	299	169	169	944)查询顾客上次的购买时间
select 
name,orderdate,cost,
lag(orderdate,1,'1900-01-01') over(partition by name order by orderdate ) as time1, lag(orderdate,2) over (partition by name order by orderdate) as time2
from test;

lag(col,n,default_value): 第三个参数传默认值,不传默认为null

tony	2017-01-02	15	1900-01-01	NULL
tony	2017-01-04	29	2017-01-02	NULL
tony	2017-01-07	50	2017-01-04	2017-01-02
jack	2017-01-01	10	1900-01-01	NULL
jack	2017-01-05	46	2017-01-01	NULL
jack	2017-01-08	55	2017-01-05	2017-01-01
jack	2017-02-03	23	2017-01-08	2017-01-05
jack	2017-04-06	42	2017-02-03	2017-01-08
neil	2017-05-10	12	1900-01-01	NULL
neil	2017-06-12	80	2017-05-10	NULL
mart	2017-04-08	62	1900-01-01	NULL
mart	2017-04-09	68	2017-04-08	NULL
mart	2017-04-11	75	2017-04-09	2017-04-08
mart	2017-04-13	94	2017-04-11	2017-04-095)查询前20%时间的订单信息
select * from (
    select name,orderdate,cost, ntile(5) over(order by orderdate) sorted
    from business
) t
where sorted = 1;

以下为不加 sorted=1 的结果
jack	2017-01-01	10	1
tony	2017-01-02	15	1
tony	2017-01-04	29	1
jack	2017-01-05	46	2
tony	2017-01-07	50	2
jack	2017-01-08	55	2
jack	2017-02-03	23	3
jack	2017-04-06	42	3
mart	2017-04-08	62	3
mart	2017-04-09	68	4
mart	2017-04-11	75	4
mart	2017-04-13	94	4
neil	2017-05-10	12	5
neil	2017-06-12	80	5
  • 排序函数
rank() 排序相同时会重复,总数不变  1 2 3 3 5
dense_rank() 排序相同时会重复,总数会减少 1 2 2 3 4
row_number() 根据顺序计算  1 2 3 4 5

select
	subject,score,
	rank() over(partition by subject order by score desc) rp,
	dense_rank() over(partition by subject order by score desc) drp,
	row_number() over(partition by subject order by score desc) rmp
from
	score;
	
subject score  rank  dense_rank  row_number
数学		95		1		 1			1
数学		86		2		 2			2
数学		85		3		 3			3
数学		56		4		 4			4

英语		84		1		 1			1
英语		84		1		 1			2
英语		78		3		 2			3
英语		68		4		 3			4

语文		94		1		 1			1
语文		87		2		 2			2
语文		65		3		 3			3
语文		64		4		 4			4
  • 自定义函数
    当hive提供的内置函数无法满足业务处理需要时,此时就可以考虑使用用户自定义函数
  1. UDF函数: 一进一出
  2. UDAF函数: 聚集函数,多进一出,类似于count/max/min
  3. UDTF函数: 一进多出,类似于 lateral view explore()
  4. 自定义UDF函数
    ① 继承org.apache.hadoop.hive.ql.exec.UDF
    ② 需要实现evaluate函数;evaluate函数支持重载
    ③ 打成jar包上传到服务器
    ④ 将jar包添加到hive,hive命令行执行: add jar jar_path;
    ⑤ 创建临时函数与开发好的java class关联
    create temporary function func_name as jar_class;
    ⑥ 如果想要创建永久函数,则需要把jar包上传到hdfs,然后在hive执行以下命令
    create function func_name as jar_class useing jar jar_hdfs_path;

hive 文件存储

hive支持的存储数据的格式主要有: textfile sequencefile orc parquet

  1. 列式存储和行式存储
    大数据-hive入门详解_第2张图片
    左边为逻辑表,右上为行式存储,右下为列式存储
  2. 当查询满足条件的一整行数据的时候,列式存储需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快
  3. 列式存储的每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量,每个字段的数据类型是相同的,列式存储可以针对性的设计更好的压缩算法
  4. textfile 和 sequencefile的存储格式都是基于行存储的,orc 和 parquet 是基于列式存储的
  5. 默认格式是textfile格式,数据不压缩,磁盘开销大,数据解析开销大
  6. orc格式是hive 0.11版本里引入的新的存储格式.每个orc文件由一个或者多个stripe组成,每个stripe 里有三部分组成,分别是index data,Row data,stripe footer.
    大数据-hive入门详解_第3张图片
    ① index data: 轻量级的index,默认是每隔1W行做一个索引
    ② row data: 存的是具体的数据,先取部分行,然后对这些行按列进行存储,对每个列进行编码,分成多个stream存储
    ③ stripe footer: 存的是各个stream的类型长度等信息
    每个文件有一个file footer,存的是每个stripe的行数,每个column的数据类型等信息,每个文件的尾部是一个postScript,这里记录了整个文件的压缩类型以及filefooter的长度信息等.在读取文件时,会seek到文件尾部读PostScript,从里面解析到file footer长度,再读filefooter,从里面解析到各个stripe信息,再读各个stripe,即从后往前读.
  7. parquet格式
    parquet是面向分析型业务的列式存储格式,以二进制方式存储,所以是不可直接读取的,文件中包括了数据和元数据,因此parquet格式文件是自解析的.通常情况下,在存储parquet数据的时候会按照block大小设置行组的大小,由于一般情况下每个mapper任务处理数据的最小单位是一个block,这样可以把每一个行组由一个mapper任务处理,增大任务执行并行度.
    大数据-hive入门详解_第4张图片
    一个文件中可以存储多个行组,文件的首位都是该文件的magic code,用于校验它是否是一个parquet文件,footer length记录了文件元数据的大小,通过该值和文件长度可以计算出元数据的偏移量,文件的元数据中包括每一个行组的元数据信息和该文件存储数据的schema信息.除了文件中每个行组的元数据,每个页的开始都会存储该页的元数据,在parquet中,有三种类型的页: 数据页,字典页和索引页.数据页用于存储当前行组中该列的值,字典页存储该列值的编码字典,每一个列块中最多包含一个字典页,索引页用来存储当前行组下该列的索引.

你可能感兴趣的:(#,大数据,hive,大数据,数据库)