目录
1、Hive产生背景及作用
2、Hive的数据组织形式
3、Hive的视图
4、数据存储
5、Hive的DDL 和 DML 操作
6、Hive的数据类型
7、Hive的函数
8、内置函数
9、自定义函数:Java语言实现
10、json解析
11、多字节分隔符
12、transform的方式???
13、Hive的beeline连接
14、Hive的Shell
15、Hive的执行过程
16、Hive的数据倾斜
17、Hive的优化
补充知识
Hive的练习
背景:MapReduce开发成本过高
Hive是什么:底层数据存储在HDFS上,执行引擎是MapReduce
Hive的架构:
安装Hive:https://blog.csdn.net/qq_1018944104/article/details/84346764#2.Hive%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA
库:和关系型数据库中的库概念一致,便于数据库管理,将不同模块的数据存储在不同的数据库中。
表:和关系型数据库中的表概念一致,也是二维表。
Hive中表的分类:
根据属性分为 内部表 和 外部表
根据功能分为 分区表 和分桶表
分区表(不同于MapReduce的分区)
1、当数据量比较大的时候,在进行查询时,如果每一次都进行全表扫描,必然造成查询性能低,这时就出现了分区表的概念。
2、分区表是将原表的原始数据进行分目录存储 ,相当于对原始表进行一个区块划分,将原来的表分成的很多的区域,这样做目的就是便于查询,在查询的时候可以减少查询的范围。
3、分区表的表现形式:
非分区表的数据存储:hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu(表存储目录),非分区表一个表的所有数据都存储在表的对应目录下,非分区表中一个表对应一个目录。
分区表:将表中的数据分别存储在不同的区下面,将表中的不同区的数据分别存储在不同的目录下。分区表指定一个分区字段,分区字段选择的依据查询的过滤字段。比如:学生信息表 查询的时候通常按照班级查询 分区字段班级
班级 3个 7年级 8年级 9年级
hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/class=7/
hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/class=8/
hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/class=9/
分区表这里一个分区就会对应一个目录结构,我们在查询的过程中,按照分区字段进行过滤查询,这个时候只会扫描指定分区字段值的目录,select * from stu where class=8; 只会扫描 hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/class=8/
分区表总结:
1)分区表对应的不同的目录结构
2)分区表的作用 减少查询的时候的数据扫描范围 提升查询性能
分桶表(类似于mapreduce的分区的概念)
1、作用:1)提升抽样的性能;2)提升join的性能
数据量比较大的时候,先进行数据抽样,抽取样本数据测试,样本数据的要求具有代表性,抽取足够散列,桶表数据就是这样的数据,可以直接拿一个桶的数据作为样本数据,分桶可以提升join的性能。
2、目录划分:将不同的桶的数据进行分别存储在不同的文件中。分桶表来说,就是选择一个分桶字段(mapreduce中的分区字段),选择完成分桶字段之后指定桶的个数(分区的个数/reducetask的个数)。
3、分桶的数据划分:分桶字段.hash%分桶个数
余数0----桶0
余数1----桶1
4、最后数据目录:hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu,按照age分桶 3个桶
hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/part-r-00000 age.hash%3=0
hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/part-r-00001
hdfs://hadoop01:9000/user/hive/warehouse/test.db/stu/part-r-00002
视图的作用主要为了提升hql语句的可读性:
1)hive中的视图只存在逻辑视图,不存在物化视图
逻辑视图:只存储视图代表的hql语句,不会进行执行
物化视图:将视图对应的查询语句执行出来结果
2)hive的视图相当于一条查询语句的快捷方式
3)hive中的视图,在查询视图的时候才会真正的执行
create view age_view as select * from stu where age>18;
select * from age_view; 这个时候视图才会执行
4)hive中的视图不支持insert、update 等操作,只支持查询操作
特点:
1.只有逻辑视图 没有物化视图
2.不支持增删改 只支持查询
3.视图相当于一个hql的快捷方式
4.视图在查询视图的时候才会真正的执行
5.hive的视图存储的时候 存在元数据库中仅仅存储的视图代表的sql语句
操作:
1.创建视图 ****
create view viewname as select ...
create view age_19 as select * from student_test where age>19;
2.显示视图列表
show tables; 展示当前数据库下的所有的表及视图
show views; 只能显示视图
3.显示视图描述信息
desc viewname;
desc formatted viewname;
4.查询视图 将视图看做普通表 ****
select * from age_19;
5.删除视图
drop table viewname ; 不可用
drop view viewname;
1、元数据存储
元数据是指Hive中库、表的描述信息,存储在关系型数据库中, 默认derby,但一般使用MySQL。
元数据对应的mysql的位置:
元数据的结构:
hive中的数据库的描述信息:
DBS表存储的是hive的数据库的描述信息
原始数据的hdfs的存储目录
6 hdfs://hadoop01:9000/user/hive/warehouse/test_bd1808.db test_bd1808 hadoop USER
每当hive中创建一个数据库的时候 这个表中就会添加一条数据
hive中的表的描述信息的元数据:
TBLS
所属数据库的id 表名 表类型
1 1542194091 2 0 hadoop 0 1 stu MANAGED_TABLE 0
每当在hive中创建一个表 这个表中就会添加一条数据
hive表的字段的描述信息:
COLUMNS_V2
所属表id 字段名 字段类型 字段顺序
1 course string 0
表中每当添加一个字段 这个表中就会添加一条数据
注意:元数据信息可以直接在MySQL中进行修改,一旦元数据修改会造成Hive表结构修改,Hive的表结构(库信息 表信息 字段信息)相关数据从元数据库加载的,所以元数据信息慎重修改。
2、原始数据存储
原始数据:表中存储的数据
存储在hdfs上,默认位置/user/hive/warehouse/
读取的配置文件:hive-default.xml
修改:在hive-site.xml添加配置项
修改完成后重新进入hive的客户端就可以了。注意 :修改生效从修改之后创建的库、表的数据目录才会修改,之前的是不会修改的。
原始数据存储的目录结构:/user/hive/warehouse/test.db/stu,当前目录下存储的是该表对应的原始数据文件。
hive的原始数据存储的配置:
1)hive-default.xml 2)hive-site.xml 3)建表语句 LOCATION
加载顺序:1)---2)---3)
生效规则:最后加载的最终生效
具体内容:https://blog.csdn.net/qq_1018944104/article/details/85272151
库的操作:
1)创建数据库:create database name;
2)切换库:use name;
3)查看库列表:show databases; 或 show databases like 'test*';
4)查看数据库的描述信息:desc database name; 或 desc database extended db_name; #查看数据库的详细信息
5)查看正在使用的库:select current_database();
6)删除库
drop database name; 只能删除空的
drop database name restrict; 严格模式下的删除库,会进行库的检查,如果删除库不是空的不允许
drop database name cascade; 删除非空数据库,级联删除
防报异常操作:
创建库为了防止异常:create database if not exists test;
删除库为了防止异常:drop database if exists test;
注:这两个操作同样适用于表和分区的操作。
表的操作:
1)创建表
2)查看表的描述信息
3)查看表的列表
4)表的修改
5)表数据的清空
6)删除表
7)查看详细建表语句
基本数据类型:--- java语言
tinyint smallint int bigint
boolean
float
double
string
timestamp
复杂数据类型:
1)array:数组类型 一组数据的,数据单一 类型一致的时候使用
数据:id names
1 zs,xsz,gs
2 ls,xlz,yl,dg
3 ww,xw
字段:id int
names array
建表语句:create table test_array(id int,names array
collection items terminated 指定集合元素之间的分割符
数据加载:load data local inpath '/home/hadoop/tmpdata/test_array' into table test_array;
结果:1 ["zs","xsz","gs"]
访问:通过下标 从0开始的 例如 select id,names[2] from test_array;
2)map 映射 key-value类型的
数据:id family
1 dad:zs,mum:hanmeimei
2 sister:lily,brother:john,mum:Alice
字段:id int
family map
建表语句: create table test_map(id int,family map
数据加载:load data local inpath '/home/hadoop/tmpdata/test_map' into table test_map;
访问:通过key找value [] ,例如 select id,family["mum"] from test_map;
map keys terminated by map集合中的key value之间的分割符
注意:分割符指定的时候由外向里指定的,即大——>小
3)struct 类似于java中的对象类型 每一个对象----Class
struct用于存储一组具有相同结构的数据,相同结构指具有相同的列数,每一列对应的含义是一致的。
数据:id stuinfo
1 zs,23,xian
2 ls,20,wuhan
3 ww,19,sichaun
字段:id int
stuinfo struct
结构体定义:
class stu{
string name;
int age;
string jiguan;
}
建表语句:create table test_struct(id int,stuinfo struct
加载数据:load data local inpath '/home/hadoop/tmpdata/test_struct' into table test_struct;
访问:对象类型的访问,对象.属性,例如 select id,stuinfo.name from test_struct;
练习题目:
id courses hobby info
1 sx:23,yw:67 sleep,chi zxm,28,jx
2 ty:99,yy:89 singing,sport,sleep cx,35,sz
建表关联 并进行数据导入
MySQL中也有函数,比如 sum、avg、max、min、count,为了便于数据处理和统计分析就出现了函数。
按照来源分: 内置函数 和 自定义函数
内置函数:hive中自带的函数
show functions; 查看所有的内置函数
desc function name; 查看函数的描述信息,比如 floor(参数) - Find the largest integer not greater than x
desc function extended name; 查看函数的详细描述信息,带案例的描述信息
常用的内置函数: 这个很重要,收集资料来搞定
1、数值类型:
round(需要处理的数据,[位数]) 四舍五入取近似值
floor 向下取整
ceil 向上取整
rand([seed]) rand() 取随机值
2、字符串:
字符串的起始下标从1开始的,从左侧向右侧
字符串也可以从右向左访问 -1
字符串截取:substr(需要处理的字符串,起始下标,截取长度) 或 substring
字符串拼接:concat 或 concat_ws 年-月-日
字符串的切分:split 返回的类型数组类型 这也是获取数组类型的一种方式
字符串查找:instr(str,substr) 存在则返回子字符串第一个字符所在的位置 >0的数,不存在则返回0
字符串替换:replace(str, search, rep)
if(判断条件,返回1,返回2) 三目表达式
select if(names[2] is not null,names[2],"dlreba") from test_array;
处理null值的函数:nvl(查询的字段, 默认值),如果第一个参数为null 则返回第二个参数的值,不为null 则返回第一个参数
类型转换的函数:cast(需要处理的数据 as 处理成的类型),例如 select cast("1" as int);
求字符串长度:length
3、集合生成函数:
array 数组生成函数
array_contains 判断数组中是否存在某个元素
map 映射生成函数 参数必须是偶数个
奇数位置的 key 偶数位置 value
4、日期处理函数:
unix_timestamp(date[, pattern]) 生成时间戳的
将给定的日期转换为时间戳
unix_timestamp() 获取当前系统的时间戳
current_timestamp() 获取当前系统的时间戳
select unix_timestamp('2018-11-11','yyyy-MM-dd');
时间戳转日期
from_unixtime(unix_time, format)
select from_unixtime(1541865600,"yyyy/MM/dd HH:mm:ss");
year 取给定的日期或时间戳的年份
month 取月
day 取日期的
hour 取小时
weekofyear select weekofyear("2018-11-22");
5、表生成函数:进一路出多路
explode(array|map) : explode炸裂完成之后可以看做一个表
1)将数组多个元素炸裂到多行,每一个元素放在一行,每一行只有一列
[1,2,3]
1
2
3
2)将map集合的元素炸裂到多行,每一个元素一行,每一行都有两列 key 和 value
{1:2,3:4}
1 2
3 4
select id,explode(names) from test_array; 相当于 select id,表 from 表; 后者是不允许的
UDTF's are not supported outside the SELECT clause
解决:当explode和其他字段一起查询时,explode函数炸裂出来的内容可以当做一个表
select id,v.* from test_array lateral view explode(names) v;
select id,v.* from test_map lateral view explode(family) v;
select id,v.* from test_map lateral view explode(family) v as 列别名;
select id,v.vk from test_map lateral view explode(family) v as vk,vv;
虚拟视图的名字v
vk vv
dad zs
mum hanmeimei
explode函数和表中的普通字段一起查询的时候,一定要注意将explode炸裂的内容放在一个虚拟lateral view中。
分析函数:数据统计分析的时候用的
row_number 添加行号的(分组求topN),必须和over子句一起用,而 over子句是用于添加规则的。
语法:row_number() over(分组条件,排序条件)
over子句中可以放两种形式:
1)distribute by 指定分桶 sort by 指定排序
2)partition by 指定分桶 order by指定排序
在每一组中进行顺序添加行号。
案例:学生信息表的每一个部门中年龄最大的前2个
分组:部门 排序:年龄,求的是局部的年龄最大的
select *,row_number() over(distribute by department sort by age desc) from student_test;
执行过程:先按照指定的分桶(分区)规则进行分桶/分区
再在每一个分桶/分区中按照指定的排序规则进行排序
最后再在每一个桶中添加行号
结果:
95006 孙庆 男 23 CS 1
95013 冯伟 男 21 CS 2
95001 李勇 男 20 CS 3
95012 孙花 女 20 CS 4
95014 王小丽 女 19 CS 5
95010 孔小涛 男 19 CS 6
95008 李娜 女 18 CS 7
95020 赵钱 男 21 IS 1
95002 刘晨 女 19 IS 2
95004 张立 男 19 IS 3
95019 邢小丽 女 19 IS 4
95018 王一 女 19 IS 5
95017 王风娟 女 18 IS 6
95003 王敏 女 22 MA 1
95022 郑明 男 20 MA 2
95007 易思玲 女 19 MA 3
95015 王君 男 18 MA 4
95011 包小柏 男 18 MA 5
95009 梦圆圆 女 18 MA 6
95005 刘刚 男 18 MA 7
95021 周二 男 17 MA 8
最终的语句:
select * from (select *,row_number() over(distribute by department sort by age desc) index
from student_test) t where index<=2;
应用场景:分组求topN
添加排名:rank 和 dense_rank,也要over子句一起用,over子句指定分桶/分区的依据,指定排序的依据
例子:求每个部门的按年龄降序添加排名
select *,rank() over(distribute by department sort by age desc) from student_test;
结果: 排名的时候将并列的进行直接累加跳过
95006 孙庆 男 23 CS 1
95013 冯伟 男 21 CS 2
95001 李勇 男 20 CS 3
95012 孙花 女 20 CS 3
95014 王小丽 女 19 CS 5
95010 孔小涛 男 19 CS 5
95008 李娜 女 18 CS 7
select *,dense_rank() over(distribute by department sort by age desc) from student_test;
结果:顺序添加排名的 有并列的不进行排名名次累加的
95006 孙庆 男 23 CS 1
95013 冯伟 男 21 CS 2
95001 李勇 男 20 CS 3
95012 孙花 女 20 CS 3
95014 王小丽 女 19 CS 4
95010 孔小涛 男 19 CS 4
95008 李娜 女 18 CS 5
注意:Hive中271个内置函数,当内置函数无法满足业务需求的时候需要自定义函数。
Hive的自定义函数可以分为3类:
1、UDF---USER DEFINE FUNCTION 用户自定义函数
处理一条数据处理完成之后还是一条数据,字符串函数
2、UDAF---USER DEFINE AGGREGATE FUNCTION 用户定义聚合函数
一次加载多条数据 处理完成就剩一条数据,进多路出一路,比如 count、sum、max、min
3、UDTF---USER DEFINE TABLE FUNCTION 用户自定义表函数
一次加载一条数据,处理完成之后变为多条数据,比如 explode(array|map)
自定义函数实现步骤:
1)创建工程 导入依赖包
2)创建一个类继承UDF类
3)实现一个或多个名字为evaluate的方法
evaluate方法是会被hive底层执行器和解析器调用到的
evaluate 的返回值和参数是根据实际的业务需求自己定义的
参数:函数调用的时候传入的参数
返回值:函数调用完成后返回的值
hive> select round(2.3);
OK
2
round函数:
evaluate方法
参数类型:double
返回值:int
public int evaluate(double a){}
求三个数的和
4)定义完成之后,将UDF的代码 打成jar包上传到服务器
5)将jar包添加到hive的classpath下
在Hive的客户端执行:add jar /home/hadoop/tmpdata/myudf.jar;
验证:list jar/jars;
hive> list jar;
/home/hadoop/tmpdata/myudf.jar
6)创建一个临时函数 关联UDF
create temporary function three_add as 'com.ghgj.cn.testUDF.MyUDF';
验证:show functions;
使用:select three_add(1,3,4); 当使用这个自定义函数的时候 本质调用com.ghgj.cn.testUDF.MyUDF下的evaluate方法
注意:
1、临时函数的作用域只对当前客户端生效,当前客户端退出临时函数就删除了,再次进入客户端 如果还想用函数 需要重新添加,重新操作上面的5 6步。
2、同一个UDF中可以写多个evaluate方法的,调用的时候根据传入的参数的不同调用不同的evaluate方法。
3、写evaluate方法的时候注意:方法必须是public的 并且 返回值不能为void
Hive中接触的数据有很多数据都是json格式的数据。
json格式类似于java中的类的结构 对象
{
属性:值,
属性:[{},{}],
属性:{
属性:值,
属性:{
}
}
}
web 前后台之间数据传输的时候 json格式
json格式数据的解析方式:1)自定义函数;2)内置函数解析 get_json_object
用法:get_json_object(json_txt, path)
参数1:需要解析的json串
参数2:需要解析的json中的属性的位置
返回值:返回的是查询的路径对应的值
{
属性1:值,
属性2:[{},{}],
属性0:{
属性3:值,
属性:{
}
}
}
json串的目录结构:
最外层的结构叫做json的根目录 $ 根目录的对象
属性1叫做根目录的子目录 取的时候用 $.属性0.属性3 $.属性2[0]
支持的路径的表达:
$ : 根目录
. : 子节点
[] : 属性对应的值如果是数组的时候 用于取下标的 取到数组中对应的值
例如:[{"movie":"1193","rate":"5","timeStamp":"978300760","uid":"1"}] ,取rate对应的值路径:$.content[0].rate
select get_json_object('{"content":[{"movie":"1193","rate":"5","timeStamp":"978300760","uid":"1"}]}','$.content[0].rate');
Hive中的数据的分割符默认的都是单字节形式的,但是在实际中采集的数据有多字节分割符的数据,比如 1::zs::23
建表:create table test01(id int,name string,age int) row format delimited fields terminated by '::';
加载完数据并查询:1 NULL
原因:虽然表定义了多字节的分割符,但默认类中不能识别,只能识别单字节的分割符,实际分割的时候按照 :分割的。
所以,1::zs::23 分割完成:1 空 zs(null)
如何解决多字节分隔符:
1)将数据中的多字节分割符替换为单字节分割符
这种方式替换的时候要求必须足够的了解数据,防止替换的单字节在原始数据中原来已经存在。
2)可以修改源码,将源码中的单字节替换为多字节分割符,不可取, 一般不用
3)采用自定义的输入输出的格式
在建表的时候
SerDe Library: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe 只支持单字节的
InputFormat: org.apache.hadoop.mapred.TextInputFormat
OutputFormat: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
输入:正则表达式
(.*)::(.*)::(.*)
输出:在正则表达式中取
取正则表达式的第一组 第二组
1::sgds:wgw::23
2::sgds:wgw::23
3::sgds:wgw::23
解决:
解析类:正则表达式的解析
输入类:正则表达式
输出:取
create table test02(id int,name string,age int)
row format serde 'org.apache.hadoop.hive.serde2.RegexSerDe'
with serdeproperties('input.regex'='(.*)::(.*)::(.*)', 'output.format.string'='%1$s %2$s %3$s')
stored as textfile;
serde:指定解析类:org.apache.hadoop.hive.serde2.RegexSerDe
with serdeproperties 指定解析属性的
input.regex指定输入的正则表达式的
output.format.string 指定需要取的组
加载数据:
load data local inpath '/home/hadoop/tmpdata/test01' into table test02;
1|||rs|||67|||78
create table test03(id int,name string,yuwen int,shuxue int)
row format serde 'org.apache.hadoop.hive.serde2.RegexSerDe'
with serdeproperties('input.regex'='(.*)\\|\\|\\|(.*)\\|\\|\\|(.*)\\|\\|\\|(.*)', 'output.format.string'='%1$s %2$s %3$s %4$s')
stored as textfile;
Hive 解析脚本的方式。
案例:test_json中求周一到周日期间哪一天的评分人数最多?
1)将数据评分时间的字段--->周几
1)自定义函数
2)内置函数
3)用脚本方式
python脚本
#!/usr/bin/python
import sys
import datetime
for line in sys.stdin://取每一条数据
line = line.strip()
movie,rate,unixtime,userid = line.split('\t')
weekday = datetime.datetime.fromtimestamp(float(unixtime)).isoweekday()
print '\t'.join([movie, rate, str(weekday),userid])
concat_ws()
strip() trim
beeline连接是生产用常用的一种连接Hive的方式,即Hive的远程连接的方式。
远程连接hive的步骤:
1)将hive的服务端启动
命令:hiveserver2 #开启hive的服务端
2)远程使用beeline连接
命令:beeline #启动beeline工具
beeline> #进入beeline的客户端
3)进行配置hive的连接
命令:!connect jdbc:hive2://hadoop02:10000 #末尾是Hive连接的url,类比 mysql连接url jdbc:mysql://主机:3306
进行连接:
beeline> !connect jdbc:hive2://hadoop02:10000
Enter username for jdbc:hive2://hadoop02:10000: 输入安装hive的linux的用户名
Enter password for jdbc:hive2://hadoop02:10000: 输入的是安装hive的用户对应的密码
报错:
Error: Could not open client transport with JDBC Uri:
jdbc:hive2://hadoop02:10000:
Failed to open new session:
java.lang.RuntimeException:
这是hadoop的远程连接报错 hadoop的安全认证错误
org.apache.hadoop.ipc.RemoteException
(org.apache.hadoop.security.authorize.AuthorizationException)
: User: hadoop is not allowed to impersonate hadoop (state=08S01,code=0)
报错原因:
beeline连接hive的时候 实质上连接hadoop集群
hadoop集群没有远程安全验证
解决方案:
修改hadoop的配置文件 将hadoop的远程连接的安全认证添加上,具体如下:
修改hadoop集群的所有节点的以下两个配置文件
1)hdfs-site.xml
2)core-site.xml
注意:
hadoop.proxyuser.远程连接的用户.hosts
hadoop.proxyuser.远程连接的用户.groups
将上面的两个配置文件 远程发送到其他节点 **********
scp core-site.xml hadoop02:/home/hadoop/apps/hadoop-2.7.6/etc/hadoop/
scp core-site.xml hadoop03:/home/hadoop/apps/hadoop-2.7.6/etc/hadoop/
scp hdfs-site.xml hadoop02:/home/hadoop/apps/hadoop-2.7.6/etc/hadoop/
scp hdfs-site.xml hadoop03:/home/hadoop/apps/hadoop-2.7.6/etc/hadoop/
重启集群 *****
上面的修正完成,就可以
1)将hive的服务端启动
hiveserver2 开启hive的服务端
2)远程使用beeline连接
beeline 启动beeline工具
beeline> 进入beeline的客户端
进行连接
beeline> !connect jdbc:hive2://hadoop02:10000
Connecting to jdbc:hive2://hadoop02:10000
输入hive的用户名 安装hive的用户名
Enter username for jdbc:hive2://hadoop02:10000: hadoop
输入安装hive的用户的密码
Enter password for jdbc:hive2://hadoop02:10000: ******
Connected to: Apache Hive (version 2.3.2)
Driver: Hive JDBC (version 2.3.2)
Transaction isolation: TRANSACTION_REPEATABLE_READ
0: jdbc:hive2://hadoop02:10000> 连接成功 执行hql
注意:在启动hiveserver2的时候 想要启动为后台的进程
nohup hiveserver2 1>/home/hadoop/hiveserver.log 2>/home/hadoop/hiveserver.err &
nohup 表示no hang up 不要挂起
1 代表的标准输出
2 代表的是错误输出
进入hive的客户端之后
quit 退出hive的客户端
set key=value 进入hive的客户端之后进行参数设置,key: 就是 hive-default.xml 和 hive-site.xml 中的
这种参数设置只对当前的客户端生效,当前客户端退出则参数失效。
比如 set hive.exec.mode.local.auto=true;
set key 查看指定参数的值
set -v 打印所有的hive或hadoop的参数配(了解)
add FILE [file] 添加文件到hive的classpath下
add jar jarname UDF 中 添加jar文件到hive的classpath下
list FILE /files
list jar/jars 查看当前的classpath下的文件或jar包资源
! linux命令 在hive的客户端执行linux命令,仅限查询相关的一些命令
dfs [dfs cmd] ***** 在hive的客户端执行hdfs相关的命令,比如 dfs -ls /;
hadoop fs -ls / 老的命令
hdfs dfs -ls / 新的命令
hadoop/hdfs 开启hadoop/hdfs的客户端的
ddl dml
source FILE 在hive的客户端执行sql脚本的
source sql脚本的位置 执行一个sql脚本
进入hive的客户端之前
语法结构:hive [-hiveconf x=y]* [<-i filename>]* [<-f filename>|<-e query-string>] [-S]
说明:
1、-i 从文件初始化 HQL
2、-e 从命令行执行指定的 HQL
3、-f 执行 HQL 脚本
6、-hiveconf x=y(Use this to set hive/hadoop configuration variables)
7、-S:表示以不打印日志的形式执行命名操作
-hiveconf key=value
在启动hive之前设置hive的相关参数,每次只能初始化一个参数
比如 hive -hiveconf hive.exec.mode.local.auto=true
-i init 从文件初始化hive的相关参数,一次性在启动hive的时候初始化多个参数
-e hql 不进入hive客户端的情况下执行hql语句,比如 hive -e "show databases"
注意:执行查询语句的时候 表名之前一定加上库名的
hive -e "select * from yingping.users limit 10"
-f 执行一个hql语句的脚本,类似mysql中的source的作用
hive -f hql脚本的路径
cli thift server 元数据 驱动(解释 编译 优化 执行)
解释器:将hql语句----- 语法树 一组操作符构成的
查看hive语句的执行计划:
explain select
a.Gender Gender,c.Title Title,avg(b.Rating) avgrate
from
users a join ratings b on a.UserID=b.UserID
join movies c on b.MovieID=c.MovieID
where a.Gender='F'
group by a.Gender,c.Title
order by avgrate desc limit 10;
Fetch Operator 数据抓取操作符
Filter Operator 过滤的操作符
Map Join Operator 关联的操作符
Group By Operator 分组操作符
hive首先将hql语句转换成一组操作符的 树
操作符是hive处理数据的最小单元
操作符:
hdfs的读写操作 fetch
MapReduce任务
几个语句的执行过程:
1、join过程
mapjoin
大*小
小*小
将其中一个小表放在每一个maptask的节点的缓存中
在map端
setup中读取缓存中的表 放在map中 key=关联建 value=其他
map中读取另外一个表进行关联
reducejoin
users ratings
select * from users a join ratings b on a.userid=b.userid;
map端:
key:关联建
value:打标记+其他
reduce端:
两个表中关联建相同的的所有的数据
直接进行关联
2、group by的过程:
SELECT pageid, age, count(1) FROM pv_users GROUP BY pageid, age;
map端:
key:pageid, age
value: 1
reduce端:
对value求和就可以了
注意:当group by和聚合函数一起使用的时候 hive会默认进行优化,比如 sum、max、min等会先在map端执行combiner
3、去重过程:
将需要去重的字段放在map端的key中,这样的话在reduce端就会分到一组 一组中取一个。
SELECT age, count(distinct pageid) FROM pv_users GROUP BY age;
map端的key:age+pageid
hadoop(广义 hadoop,hive,hbase)不怕数据量大 怕数据倾斜
概念:进行数据计算的时候,由于数据分布不均匀,造成某一个节点上分配的数据量很多,造成这个节点的计算任务很大
表现:hive的数据倾斜,说到底就是mapreduce的数据倾斜,本质就是reduce端的数据倾斜
reduce端的数据分配取决于分区算法(默认 hash 自定义)
10个分区中,若9个分区--9reducetask处理200M的数据,另外1个分区--reducetask处理5G的数据,那么结果如下:
map 20% reduce 0%
map 80% reduce 0%
map 100% reduce 40%
map 100% reduce 90%
map 100% reduce 90%
map 100% reduce 90%
map 100% reduce 90%
map 100% reduce 90%
......................
以上展示的结果就是产生数据倾斜了。
不会产生数据倾斜的场景:
1)hive执行过程中fetch的过程都不会产生数据倾斜,fetch的过程不需要转换为MR任务。
more
select
filter where
limit
2)group by 和聚合函数一起使用的时候
group by和聚合函数一起使用的时候默认在map端执行一次聚合函数(combiner),大大减少reduce端的数据量。
会产生数据倾斜的场景:
1)聚合函数不和group by一起使用的时候
select count(*) from weibo;
weibo 3T
map端:
key:“a”
value:1
reduce端:
所有的数据分配到一个节点上 一个reducetask上
求的聚合函数就是全局的聚合,因此只能一个reducetask任务
2)count(distinct )
先去重 多个reducetask,count() 全局的
3)join——reducejoin
mapjoin 不会产生数据倾斜的
reducejoin 很大程度上会产生数据倾斜
map-key:join的关联建
log address 北京 内蒙古
user address 北京 内蒙古
9T 1T
REDUCETSK:
reducetask0---北京 9T
reducetask1---内蒙古 1T
分析场景:
1、join的时候null值过多
log----电商日志 10T userid=null 4T
userid order money product ...
user----用户注册的时候生成的
userid name address num
select * from log a join user b on a.userid=b.userid;
所有的userid=null的数据全部分配到一个reducetask中
就会产生数据倾斜
解决方案:
1)null值不参与连接
select * from (select * from log where userid is not null) a join user b on a.userid=b.userid;
select * from (select * from log where userid is not null) a join user b on a.userid=b.userid
union all
select *,null,null,null from log where userid is null;
2)给null值加随机数
null--null123
null---null234
select * from log a join user b on case when a.userid is null then concat(a.userid,rand()) then a.userid end=b.userid;
方案2优于方案1
2、join的关联建的数据类型不统一
select * from log a join user b on a.userid=b.userid;
a.userid = string
b.userid = int
hive中默认关联的时候会将string类型转换为int类型
string ---- "123" ---int 123
string ---- "123 " ---int null
解决方案:
将其中一个表的数据类型转换 将两个表的类型统一,比如 b.userid int ---- string
3、大*大关联的时候
1)大*小 小*小 hive中mapjoin reducejoin
决定hive是否启动mapjoin的是 hive.auto.convert.join=true 默认启动hive的mapjoin。
但不是所有的join都执行mapjoin,这是有文件大小限制的。
决定执行join时候,若小表的大小如果在下面的范围内则默认执行mapjoin
hive.smalltable.filesize or
hive.mapjoin.smalltable.filesize
hive.mapjoin.smalltable.filesize=25000000
也就是说,在进行join的小表大小在25M以内,则默认执行的都是mapjoin
若小表大小超过25M,则默认执行的是reducejoin
2)大*中
中表指的是表的大小超过25M的但是有不是很大的 每个节点中的缓存可以承受的
大 2T user
中 200M log
默认执行执行reducejoin
1)效率低
2)容易产生数据倾斜
那么,就强制执行mapjoin
语法:/*+mapjoin(a)*/
select /*+mapjion(a)*/* from log a join user b on a.userid=b.userid;
3)大*大
user 3T 存储的是建站以来的所有用户信息
log 某一天的日志 20G
解决方案:
1)将一个大表进行切分
分区表:将user表切成分区表, log表关联每一个分区表,大*大===n 大*小
分桶表:两个表都进行按照统一的规则进行切分大*大===n 小*小 大*小
2)将其中一个表瘦身
将其中的一个表的数据进行一步过滤,抽取出来可以进行关联的数据,将不能进行关联的数据删除。
user 3T 存储的是建站以来的所有用户信息 30亿
log 某一天的日志 20G 2000w
50W 50w 500M 500/10=50w
500w 5G/10
user表瘦身,根据log表进行瘦身如下:
第一步:求出log表中去重之后userid
select distinct userid from log;
第二步,根据第一步的结果对user表瘦身
select /*+mapjoin(a)*/b.* from (select distinct userid from log) a join user b on a.userid=b.userid;
第三步,开始最后关联
select /*+mapjion(c)*/* from (select /*+mapjoin(a)*/b.* from (select distinct userid from log) a
join user b on a.userid=b.userid) c
join log d on c.userid=d.userid;
如何获取分区表的一个分区中的数据呢?
在查询的时候将分区字段进行过滤 select * from biao where dt="20181123"
分区表在查询的时候,将分区字段作为普通字段查询就可以了。
如何获取某个桶中的数据?
语法:tablesample (bucket x out of y)
y:桶簇的个数
桶簇:一个(半个)或多个桶组成的集合
student_buk 3个桶
y=1 只有一个桶簇 这个桶簇中包含所有的桶的
y=2 分为两个桶簇 每一个桶簇包含1.5个桶的
y=3 分为3个桶簇 每一个桶簇1个桶
y=6 6个桶簇 每个桶簇3/6=0.5桶
x:代表取得数据是第几个桶簇的数据
x=1 代表取的是第一个桶簇的
y=3 取第2个桶的数据
select * from student_buk tablesample(bucket 2 out of 3);
y=6 取第2个桶的数据,6个桶簇 每个桶簇 0.5个桶的数据
select * from student_buk tablesample(bucket 2 out of 6);
select * from student_buk tablesample(bucket 5 out of 6);
y=9 取第2个桶的数据
select * from student_buk tablesample(bucket 2 out of 9);
select * from student_buk tablesample(bucket 5 out of 9);
select * from student_buk tablesample(bucket 8 out of 9);
面试题:mapreduce如何实现大数据(4T)量的全局排序?hive的order by你觉得是如何实现的?
答案:分区,范围分区
1、排序
全局排序 order by(reducetask只能是一个? 可以有多个的,性能消耗大)
局部排序
sort by 局部排序 对每一个reducetask结果进行排序的
cluster by 先根据指定的字段分桶,再在每一个桶中排序
distribute by +sort by 指定字段分桶 指定字段排序
合理选择排序
2、合理使用笛卡尔积
尽量避免使用笛卡尔积,若场景中非要做笛卡尔积,那么可以考虑以下情况
大*(只有几条数据的小表)
开启hive的笛卡尔积的开关
select * from a,b;
select * from a join b;
大*中
hive中笛卡尔积可以执行但是性能很低,map的key不好确定。
解决方案:
人为的添加关联键
1)小表的关联键随机添加
2)将大表复制多份,小表去重后关联建的个数,给每一份大表数据添加关联建都是小表的其中一个关联建
3)开始真正的关联
3、in/exists 性能低
left semi join
4、合理maptask的个数
切片
太小 maptask个数很多,大量的时间浪费在maptask启动销毁上,不划算的。
太大 maptask的并行度不够
一个切片---block---maptask
原始数据都是大量的小文件的时候,会进行一个小文件合并减少maptask的个数。
5、jvm重用
一个maptask/reducetask---->container---->yarnchild
mapred.job.reuse.jvm.num.tasks=1 默认值
默认情况下,一个container只会运行一个maptask或reducetask
set mapred.job.reuse.jvm.num.tasks=5 #一个container只会运行多个task任务
uber :false 针对maptask数据量比较小的时候,一个container中启动10个maptask,默认关闭的,参数值改为 true。
6、合理设计reducetask的个数
最多不超过 datanode*0.95
7、小文件合并
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
#执行 Map 前进行小文件合并
set hive.merge.size.per.task = 256*1000*1000 #合并文件的大小
默认情况下hive在map输入数据之前进行小文件合并的
生产上一般会提前进行手动合并,减轻hdfs的namenode压力。
8、合理进行分区和分桶
分区表:
当一个表中的数据很大的时候,这个时候为了提升表的查询性能,这个时候需要考虑将这个表建为分区表。
作用:减少以分区字段作为过滤条件的扫描范围,提升性能
student_ptn 分区字段 grade
select * from student_ptn where grade=1303; #只扫描分区1303
select * from student_ptn where yuwen>23; #全表扫描
分区字段:选经常用于过滤的字段,可以多个--多级分区
分区字段在过滤查询的时候当做普通字段处理
分桶表:
每个桶中的数据:分桶字段.hash%桶的个数
1)提升抽样的性能:直接抽取某一个桶的数据作为样本数据
2)提升join的性能:直接拿去两个表中对应的桶的数据进行关联就可以了
如何获取某个桶中的数据?
语法:tablesample (bucket x out of y)
y:桶簇的个数
桶簇:一个(半个)或多个桶组成的集合
student_buk 3个桶
y=1 只有一个桶簇 这个桶簇中包含所有的桶的
y=2 分为两个桶簇 每一个桶簇包含1.5个桶的
y=3 分为3个桶簇 每一个桶簇1个桶
y=6 6个桶簇 每个桶簇3/6=0.5桶
x:代表取得数据是第几个桶簇的数据
x=1 代表取的是第一个桶簇的
y=3 取第2个桶的数据
select * from student_buk tablesample(bucket 2 out of 3);
y=6 取第2个桶的数据,6个桶簇 每个桶簇 0.5个桶的数据
select * from student_buk tablesample(bucket 2 out of 6);
select * from student_buk tablesample(bucket 5 out of 6);
y=9 取第2个桶的数据
select * from student_buk tablesample(bucket 2 out of 9);
select * from student_buk tablesample(bucket 5 out of 9);
select * from student_buk tablesample(bucket 8 out of 9);
如何获取分区表的一个分区中的数据呢?
在查询的时候将分区字段进行过滤 select * from biao where dt="20181123"
分区表在查询的时候,将分区字段作为普通字段查询就可以了。
hive的本地模式:set hive.exec.mode.local.auto=true;
https://blog.csdn.net/qq_1018944104/article/details/85272888
hive-03中的作业包括:答案在hive-05中
1、微博案例(有讲解)
2、Hive影评案例(有讲解)
3、Hive面试题:https://blog.csdn.net/qq_1018944104/article/details/85298109