# 启动 hive
./bin/hive
# 如果启动失败,可以是因为 hive service 需要启动,需要先执行该命令
./bin/hive --service metastore &
# 帮助
./bin/hive --help
# 指定默认连接的数据库
hive --database dbName
eg:hive --database db01
// 在 linux 终端中执行 SQL 或者 HQL 语句
hive -e '命令'
eg: hive -e 'show databases'
# 在 linux shell 命令中执行一个写有 SQL 语句的文件
hive -f /opt/app/hive.sql (hive.sql是sql语句文件)
# 使当前 shell 配置临时生效
hive --hiveconf hive.cli.print.current.db=false
# 查看数据库
show databases;
# 使用数据库
use dbName
# 创建 db01 数据库
create database if not exists dbName;
# 在 hdfs 上指定目录 “dblocate” 来数据库的目录
create database if not exists dbName LOCATION '/dblocate';
如果不指定目录,那么数据库的目录为:hdfs: /user/hive/warehouse/。这也是 hive 创建数据库的默认存储路径。
# 删除数据库
drop database dbName [cascade]
# 删除表
drop table tbName;
# 查看表描述信息
desc tableName
# 查看内置函数
show functions
# 查看函数描述
desc function methodName;
# 建表之前需要指定数据库
use databaseName;
# 与 mysql 一样,创建一个 student 的表,包括两个字段: num,name
create table if not exists student(
num int,
name string
)ROW FORMAT DELIMITED FIELDS TERMINATED
BY '\t' # 申明文件分隔符
stored as textfile; 申明文件存储格式。
# 复制表结构,生成一张新的空表
create table cpTable like tbName;
# 复制结果临时表,生成一张有数据的新表。
create table cpTable as select name from tbName;
# 删除表
drop table if exists tbName;
# 清空表内容, 但保留表结构
truncate table tbName;
# 查看表
show tables in dbName;
# 修改表的名称
alter table oldTableName rename to newTableName;
# 增加列
alter table tableName add columns(col type);
# 替换全部的列
alter table tableName replace columns(col1 type1, col2 type2, col3 type3);
# 修改列
alter table tableName change oldColName newColName columnType;
# 加载数据
【overwrite,覆盖重写,之前的 hive 的数据不存在被覆盖了】
【local 表示从本地路径。去掉 local,数据时从 hdfs 上加载】
load data local inpath '/opt/datas/student.txt' [overwrite] into table tb01;
load data inpath '/student.txt' into table student;
【如果 tb 是内部表,那么数据需要先被加载到 hdfs 上,然后在被移动到 hive 表中对应的位置。】
【仅对本次hive启动有效】
set hive.cli.print.current.db=true; # 显示当前数据库名
set hive.cli.print.header=true # 显示表头
set -v # 显示所有设置
# 查看本地目录信息
hive -> !ls /; (hive 终端)
# 查看 HDFS 目录信息
hive -> dfs -ls / ;
数据类型 | 长度 | 范围 | 示例 |
---|---|---|---|
Tinyint | 1字节的整数 | -128 ~ 127 | 12 |
SmallInt | 2字节的整数 | -32768 ~ 32767 | 255 |
Int | 4字节的整数 | -2147483648 ~ 2147483647 | 250000 |
BigInt | 8字节的整数 | -9223372036854775808 ~ 9223372036854775807 | 2500000000 |
Boolean | bool类型 | true,false | TRUE |
Float | 4字节单精度浮点型 | 3.1211 | |
Double | 8字节双精度浮点型 | 3.1123123 | |
String | 字符串 | “absec” | |
TimeStamp | 时间戳,格式 yyyy-mm-dd hh:mm:ss | 支持 unix timestamp | 2019-02-03 02:21:39 |
Binary | 二级制 | 0 or 1 | |
Date | 日期,格式 yyyy-mm-dd | 可用 String 代替 | |
Decumal | 任意精度数字 | 10 | |
Varchar | 字符串 | 字符串长度只能为 1 ~ 65355 | “abcdfs” |
Char | 字符串 | 长度 1 ~ 255 | “bacdf” |
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]
1)【CREATE TABLE】 创建一个指定名字的表。如果相同名字的表已经存在,则抛出异常;用户可以用 IF NOT EXISTS 选项来忽略这个异常。
2)【EXTERNAL】 关键字可以让用户创建一个【外部表】,在建表的【同时指定一个指向实际数据的路径】(LOCATION)。Hive 创建【内部表】时,会将数据【移动】到数据仓库指向的路径;若创建【外部表】,【仅记录数据所在的路径】,不对数据的位置做任何改变。在删除表的时候,【内部表】的【元数据】和【数据】会被【一起删除】,而【外部表】只【删除元数据】,不删除数据。
3)COMMENT:为表和列添加注释。
4)PARTITIONED BY创建分区表。
5)CLUSTERED BY创建分桶表。
6)SORTED BY不常用。
7)ROW FORMAT DELIMITED [FIELDS TERMINATED BY char]
[COLLECTION ITEMS TERMINATED BY char]
[MAP KEYS TERMINATED BY char]
[LINES TERMINATED BY char]
8)STORED AS指定存储文件类型
常用的存储文件类型:SEQUENCEFILE(二进制序列文件)、TEXTFILE(文本)、RCFILE(列式存储格式文件)
如果文件数据是纯文本,可以使用STORED AS TEXTFILE。如果数据需要压缩,使用 STORED AS SEQUENCEFILE。
9)LOCATION :指定表在HDFS上的存储位置。
10)LIKE允许用户复制现有的表结构,但是不复制数据。
内部表是未被 “external” 修饰的表,其由 hive 管理。内部表由于存储了元数据以及存储数据,所以删除内部表,存储数据与元数据也会被删除。并且内部表被创建得路径在 hive 的默认仓库目录,即 “ /user/Hive/warehouse/”
create table student06(
id int,
name string,
age int) row format delimited fields terminated by '\t'
stored as textfile
location 'hdfs://......'
# 查看基表信息
show create table tbName;
# 查看表格式
desc formatted tbName;
外部表是被 “external” 修饰的表,其由 HDFS 管理。删除外部表仅仅会删除元数据,但是存储数据不会被删除。用户在创建外部表时,可以自己指定表的路径:
create external table student06(
id int,
name string,
age int)
row format delimited fields terminated by '\t'
location 'hdfs://dataPath';
# 导入数据
load data local inpath '/opt/module/datas/dept.txt' into table default.student06;
1. 内部表转化为外部表
alter table student2 set tblproperties('EXTERNAL'='TRUE');
2. 外部表转化为
alter table student2 set tblproperties('EXTERNAL'='FALSE');
内部表 | 外部表 |
---|---|
目录由 hive创建在默认的目录下 | 目录由用户自己创建表时自己用 location 来指定 |
删除表时,表的元数据与表的数据目录都会被删除 | 删除表时,只删除表的元数据而表的数据不会被删除 |
一般来源的数据会在不同的平台上进行处理,所以为了方便映射,就可以采用【外部表】来进行映射,这样即使删除掉了表,也不会删除数据。也就不会影响数据在其他平台上的操作。
分区表是在系统下创建文件夹目录,把分类数据放在不同的目录里面,可以加快查询速度。
如: 创建一个以 age 分区的表,
create table student08(
num int,
name string
)PARTITIONED BY (age string) ROW FORMAT DELIMITED FIELDS TERMINATED BY "\t";
# 导入分区数据
LOAD DATA INPATH '/stud.txt' OVERWRITE INTO TABLE student08 PARTITION (age='7');
LOAD DATA INPATH '/stud.txt' OVERWRITE INTO TABLE student08 PARTITION (age='10');
# 单分区查询
select * from stundent08 where age = 9;
# 增加表分区, 用空格分开
alter table student08 add partition(age='9') partition(age='15');
# 查看表分区详情
show partition tableName;
# 导入分区数据
insert into student08 partition(age='11') select * from student08 where age='9';
# 删除表分区 -- 子目录和数据都会被删除
alter table tableName drop partition(age='11');
分区表中,表目录里面有多个子目录。如果要针对不同时间,格式等要求创建表,那么可以创建分区表。
分区表的数据是存放在不同的子目录中,在查询的时候,既可以针对子目录进行扫描,也可以针对全表进行扫描。
创建分区表后,分区目录看不到子目录,只有在导入数据才可以看到。
load data [local] inpath '/opt/module/datas/student.txt' [overwrite] into table student [partition (partcol1=val1,…)];
1)load data:表示加载数据
2)local:表示从本地加载数据到hive表(【复制】);否则从HDFS加载数据到hive表(【移动】)
3)inpath:表示加载数据的路径
4)overwrite into:表示覆盖表中已有数据,否则表示追加
5)into table:表示加载到哪张表
6)student:表示具体的表
7)partition:表示上传到指定分区
load data local inpath 'local_path' into table tb_name;
load data inpath 'hdfs_path' into table tb_name;
# 将从 oldTbName的查询结果保存到 newTbName 上
create table newTbName as select * from oldTbName;
# 根据查询结果插入到表中
insert into table tbName select * from tb1_name;
# 基本插入数据
insert into table student partition(age='11') values(1,'wangwu');
# 多数据插入
from student
insert overwrite table student partition(age='15')
select id, name where age='10'
insert overwrite table student partition(age='20')
select id, name where age='23';
create table if not exists student5(
id int, name string
)
row format delimited fields terminated by '\t'
location 'hdfs_table_name';
dfs -put local_file_path hdfs_table_name;
import table student2 partition(age='15') from
'/user/hive/warehouse/export/student';
# 将查询到的结果导出到指定的位置。
insert overwrite local directory '/opt/module/datas/export/student' select * from student;
insert overwrite local directory '/opt/module/datas/export/student1' ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' select * from student;
insert overwrite directory '/user/atguigu/student2' ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' select * from student;
# hdfs dfs -get 在 Linux 控制台操作
[hdfs] dfs -get /user/hive/warehouse/student/month=201709/000000_0 /opt/module/datas/export/student3.txt
# Linux 控制台操作
bin/hive -e 'select * from default.student;' > /opt/module/datas/export/student4.txt;
# 在 hive 控制台操作
export table default.student to '/user/hive/warehouse/export/student';
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]
order by:全局排序
order by 会对查询的全局结果进行排序。最终 map 数据的数据汇聚到【一个 reduce】 中去执行。如果数据量很大,那么这个操作是相当漫长的。所以在 hive 操作中【尽量少用 order by】,除非数据量很小。
sort by : 局部排序
sort by 是局部排序操作,也就是说在【每个 reduce】 都会进行排序,可以保证每个reduce 中的数据是有序的。但是对于全局而言,其又不一定是有序的。
distribute by :根据指定字段分区
distribute by 是指定输出结果怎样划分到各自的 reduce 分区中。对于distribute by进行测试,一定要分配多reduce进行处理,否则无法看到distribute by的效果。
cluster by
cluster by 具有 sort by 与 distribute by 的两重功能,能将相同字段进行 sort by 排序和distribute by 分区,但是只能倒序排序。
select * from student08 order by age;
select * from student08 sort by age;
set mapreduce.job.reduces=3;
select * from student08 distribute by age sort by name desc;
select * from student08 cluster by age;
select * from student08 distribute by age sort by age;
# 设置每个 reduce 处理的数据量
hive.exec.reducers.bytes.per.reduce=
# 设置最大能够运行的 reduce 个数
hive.exec.reducer.max=
# 实际 reduce 的个数
mapreduce.job.reduces=
# 设置 reduce 开启条件
hive.fetch.task.conversion=none,more,min
CONCAT(string A/col, string B/col…):返回输入字符串连接后的结果,支持任意个输入字符串;
CONCAT_WS(separator, str1, str2,...):它是一个特殊形式的 CONCAT()。第一个参数是剩余参数间的分隔符。
select name, concat(constellation, ",", blood_type) base from person_info
用法:LATERAL VIEW udtf(expression) tableAlias AS columnAlias
udtf 包括: split,explode 等 UDTF,将一列数据拆成多行数据。
EXPLODE(col):将hive一列中复杂的array或者map结构拆分成多行
select
movie,
category_name
from
movie_info lateral view explode(category) table_tmp as category_name;
1. from 加载 movie_info 的表文件,生成一张临时表;
2. lateral view explode 基于from 的临时表,将 category 列数据拆分成多行, 并生成一张临时表。临时表别名为 table_tmp,而新生成的临时表的当前 category 的列名的别名为 category_name;
3. 通过 select 对 lateral view 的临时表进行查询操作。
格式:聚合函数 + over()
OVER():指定分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变而变化。不加参数默认全表,和当前行没有关系。
当同一个select查询中存在多个窗口函数时,他们相互之间是没有影响的.每个窗口函数应用自己的规则.
在SQL处理中,窗口函数都是最后一步执行,而且仅位于Order by字句之前。
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返回此行所属的组的编号。注意:n必须为int类型。
2.数据准备: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
3.需求
(1)查询在2017年4月份购买过的顾客及总人数(注意了,不是购买次数)
(2)查询顾客的购买明细及月购买总额
(3)上述的场景,要将cost按照日期进行累加
(4)查询顾客上次的购买时间
(5)查询前20%时间的订单信息
4.创建本地business.txt,导入数据
[atguigu@hadoop102 datas]$ vi business.txt
5.创建hive表并导入数据
create table business(
name string,
orderdate
string,cost int
) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
load data local inpath "/opt/module/datas/business.txt" into table business;
6.按需求查询数据
(1)查询在2017年4月份购买过的顾客及总人数
select name,count(*) over ()
from business
where substring(orderdate,1,7) = '2015-04'
group by name;
(2)查询顾客的购买明细及月购买总额
select name,orderdate,cost,sum(cost) over(partition by month(orderdate)) from
business;
select name,orderdate,cost,sum(cost) over(distributed by month(orderdate)) from
business;
(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 business;
(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 business;
(5)查询前20%时间的订单信息
select * from (
select name,orderdate,cost, ntile(5) over(order by orderdate) sorted
from business
) t
where sorted = 1;
子查询必须写具体字段,不能用*代替。
ROW_NUMBER() | RANK() | DENSE_RANK() |
---|---|---|
排序,根据查询出的顺序依次标注排名,没有重复。如 1,2,3 | 排序,相同数据标注相同的排名,而下一个不同的数据则被跳跃标注。如,1,1,3 | 排序,相同数据标注相同的排名,下一条不同的数据直接依次标注。如1,1,2 |
select name,
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;
public class UserInfoParser extends UDF{
public String evaluate(String field,int index){
String replaceAll = field.replaceAll("\\|", " ");
String[] split = replaceAll.split(" ");
return split[index-1];
}
}
// 创建临时函数
create temporary function functionName as 'packageName.className';
虚拟列并不是在表中真正存在的列,其用意是为了将 hive 中的表进行分区,这对每日增长的海量数据存储而言非常有用的。
其种类有两种:
select *, INPUT__FILE__NAME from student;
select *, BLOCK__OFFSET__INSIDE__FILE from student;
Hive 压缩的目的是:减少磁盘 IO 与网络 IO
zlib -> org.apache.hadoop.io.compress.DefaulCodec
gzip -> org.apache.hadoop.io.compress.GzipCodec
Bzip2 -> org.apache.hadoop.io.compress.BZip2Codec
Lzo -> com.hadoop.compression.lzo.LzoCodec
Lz4 -> org.apache.hadoop.io.compress.Lz4Codec
Snappy -> org.apache.hadoop.io.compress.SnappyCodec
# MapReduce 配置 map 端压缩:
set mapreduce.map.output.compress=true
set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec
# MapReduce 配置 Reduce 端压缩:
set mapreduce.output.fileoutputformat.compress=true
set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.SnappyCodec
# Hive 配置压缩
set hive.exec.compress.intermediate=true
set mapred.map.output.compression.codec=org.apache.hadoop.io.compress.SnappyCodec
set mapred.output.compression.type=BLOCK
# 任务中间压缩
set hive.exec.compress.intermediate=true
set hive.intermediate.compression.codec=org.apache.hadoop.io.compress.SnappyCodec
set hive.intermediate.compression.type=BLOCK
Fetch抓取是指,Hive中对某些情况的查询可以不必使用MapReduce计算。
可以针对查询进行不执行 MR 来进行处理,参数:hive.fetch.task.conversion = more
,表示在 select,where,limit 操作时,都直接进行数据抓取操作,而不涉及 MR 计算的操作,这样执行的效率更快更高。
set hive.fetch.task.conversion= more;
对于小数据集,Hive 可以通过本地模式在单台机器上处理所有的任务,执行时间可以明显被缩短。
set hive.exec.mode.local.auto = true => 动启动这个优化
set hive.exec.mode.local.auto.inputbytes.max=50000000; => 数据大小不能超过 128 MB
set hive.exec.mode.local.auto.input.files.max=10; => mr的最大输入文件个数,默认为 4。
新版的hive已经对小表JOIN大表和大表JOIN小表进行了优化。小表放在左边和右边已经没有明显区别。
如果空 key 对应的数据是异常数据,我们需要在SQL语句中进行过滤。
有时虽然某个 key 为对应的数据很多,但是相应的数据不是异常数据,必须要包含在 join 的结果中,此时我们可以表 a 中 这个 key 的值赋一个随机的值,使得数据随机均匀地分不到不同的reducer上。
select n.* from nullidtable n full join ori o on
case when n.id is null then concat('hive', rand()) else n.id end = o.id;
可以用MapJoin把【小表】全部加载到内存在map端进行join,避免reducer处理。
set hive.auto.convert.join = true; 默认为true
set hive.mapjoin.smalltable.filesize=25000000;
select b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
from smalltable s
join bigtable b on s.id = b.id;
并不是所有的聚合操作都需要在Reduce端完成,很多聚合操作都可以先在Map端进行部分聚合,最后在Reduce端得出最终结果。
hive.map.aggr = true # 是否在Map端进行聚合,默认为True,需要更多的内存
hive.groupby.mapaggr.checkinterval = 100000 # 在Map端进行聚合操作的条目数目
hive.groupby.skewindata = true # 有数据倾斜的时候进行负载均衡(默认是false)
当 hive.groupby.skewindata
选项设定为 true,生成的查询计划会有两个MR Job。第一个MR Job中,Map的输出结果会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的Group By Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;第二个MR Job再根据预处理的数据结果按照Group By Key分布到Reduce中(这个过程可以保证相同的Group By Key被分布到同一个Reduce中),最后完成最终的聚合操作。
数据量大的情况下,由于COUNT DISTINCT操作需要用一个Reduce Task来完成,这一个Reduce需要处理的数据量太大,就会导致整个Job很难完成,一般COUNT DISTINCT使用先GROUP BY再COUNT的方式替换。
select count(distinct(id) ) from bigtable ;
select count(id) from (select id from bigtable group by id) a;
尽量避免笛卡尔积。
谓词下推:先执行where语句
列处理:在SELECT中,只拿需要的列,如果有,尽量使用分区过滤,少用SELECT *。
行处理:在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在Where后面,那么就会先全表关联,之后再过滤。
hive.exec.dynamic.partition=true # 开启动态分区功能
hive.exec.dynamic.partition.mode=nonstrict # 设置为非严格模式
hive.exec.max.dynamic.partitions=1000 # 在所有执行MR的节点上,最大一共可以创建多少个动态分区
hive.exec.max.dynamic.partitions.pernode=100 # 在每个执行MR的节点上,最大可以创建多少个动态分区
hive.exec.max.created.files=100000 # 整个MR Job中,最大可以创建多少个HDFS文件
hive.error.on.empty.partition=false # 当有空分区生成时,是否抛出异常。一般不需要设置。
数据倾斜造成的原因:当到达 reduce 端数据如果在某一个 key 上分布特别多的话,就会造成单个节点处理时间异常增多,从而导致整体任务消耗严重。
在map执行前合并小文件,减少map数:CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)。HiveInputFormat没有对小文件合并功能。
set hive.input.format= org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
set mapreduce.input.fileinputformat.split.maxsize=100;
set mapreduce.job.reduces = 15;
通过设置参数hive.exec.parallel值为true,就可以开启并发执行。不过,在共享集群中,需要注意下,如果job中并行阶段增多,那么集群利用率就会增加。
set hive.exec.parallel=true; //打开任务并行执行
set hive.exec.parallel.thread.number=16; //同一个sql允许最大并行度,默认为8。
hive.mapred.mode
strict
mapreduce.job.jvm.numtasks
10
hive.mapred.reduce.tasks.speculative.execution
true