大数据-hive入门详解
目录hive概述什么是hive?HQL转换成MR流程hive的优缺点hive的架构hive概述什么是hive?维基百科-hiveApache Hive 中文手册hive是建立在hadoop架构上的数据仓库工具,能够提供数据精炼、查询和分析,能够将结构化的数据文件映射成一张表,并提供类SQL查询功能,底层是将SQL转换成MR程序HQL转换成MR流程首先hive通过对SQL进行统计分析,将SQL语言中
·
hive概述
什么是hive?
- 维基百科-hive
- Apache Hive 中文手册
- hive是建立在hadoop架构上的数据仓库工具,能够提供数据精炼、查询和分析,能够将结构化的数据文件映射成一张表,并提供类SQL查询功能,底层是将SQL转换成MR程序
HQL转换成MR流程
- 首先hive通过对SQL进行统计分析,将SQL语言中常用的操作用MapReduce写成很多模板,所有的MapReduce模板都封装在hive中
- 用户根据业务逻辑编写HQL语句
- 通过hive框架匹配出响应的MapReduce模板
- 运行MapReduce程序,生成相应的结果返回给客户端
hive的优缺点
- 优点
- 操作接口采用类SQL语法,提供快速开发能力(简单易上手)
- 避免了写MapReduce程序,减少了开发人员的学习成本
- hive适合处理大数据
- 支持用户自定义函数,用户可以根据自己的需求实现自己的函数
- 缺点
- hive的SQL表达能力有限,迭代式算法无法表达,不擅长数据挖掘
- hive的执行延时较高,适合实时性不高的场合,处理小数据没有优势
- hive自动生成的MapReduce通常情况下不够智能,调优比较困难,粒度较粗
hive的架构
- 用户接口 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 布尔类型,true或false
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): 日期加1天 2020-08-09
date_sub('2020-08-08',1): 日期减1天 2020-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为1的
select
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)查询17年4月份购买过的顾客及总人数
select
name,count(*) over()
from
test
where
substring(orderdate,1,7) = '2017-04'
group by
name;
jack 2
mart 2
(2)查询顾客的购买明细及月购买总额
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 80
(3)上述的场景,要将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 94
(4)查询顾客上次的购买时间
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-09
(5)查询前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提供的内置函数无法满足业务处理需要时,此时就可以考虑使用用户自定义函数
- UDF函数: 一进一出
- UDAF函数: 聚集函数,多进一出,类似于count/max/min
- UDTF函数: 一进多出,类似于 lateral view explore()
- 自定义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
- 列式存储和行式存储
左边为逻辑表,右上为行式存储,右下为列式存储 - 当查询满足条件的一整行数据的时候,列式存储需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快
- 列式存储的每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量,每个字段的数据类型是相同的,列式存储可以针对性的设计更好的压缩算法
- textfile 和 sequencefile的存储格式都是基于行存储的,orc 和 parquet 是基于列式存储的
- 默认格式是textfile格式,数据不压缩,磁盘开销大,数据解析开销大
- orc格式是hive 0.11版本里引入的新的存储格式.每个orc文件由一个或者多个stripe组成,每个stripe 里有三部分组成,分别是index data,Row data,stripe footer.
① index data: 轻量级的index,默认是每隔1W行做一个索引
② row data: 存的是具体的数据,先取部分行,然后对这些行按列进行存储,对每个列进行编码,分成多个stream存储
③ stripe footer: 存的是各个stream的类型长度等信息
每个文件有一个file footer,存的是每个stripe的行数,每个column的数据类型等信息,每个文件的尾部是一个postScript,这里记录了整个文件的压缩类型以及filefooter的长度信息等.在读取文件时,会seek到文件尾部读PostScript,从里面解析到file footer长度,再读filefooter,从里面解析到各个stripe信息,再读各个stripe,即从后往前读. - parquet格式
parquet是面向分析型业务的列式存储格式,以二进制方式存储,所以是不可直接读取的,文件中包括了数据和元数据,因此parquet格式文件是自解析的.通常情况下,在存储parquet数据的时候会按照block大小设置行组的大小,由于一般情况下每个mapper任务处理数据的最小单位是一个block,这样可以把每一个行组由一个mapper任务处理,增大任务执行并行度.
一个文件中可以存储多个行组,文件的首位都是该文件的magic code,用于校验它是否是一个parquet文件,footer length记录了文件元数据的大小,通过该值和文件长度可以计算出元数据的偏移量,文件的元数据中包括每一个行组的元数据信息和该文件存储数据的schema信息.除了文件中每个行组的元数据,每个页的开始都会存储该页的元数据,在parquet中,有三种类型的页: 数据页,字典页和索引页.数据页用于存储当前行组中该列的值,字典页存储该列值的编码字典,每一个列块中最多包含一个字典页,索引页用来存储当前行组下该列的索引.
更多推荐
所有评论(0)