Hive知识点

Hive

  • 知识点结构图
    在这里插入图片描述

一、Hive概述

1.1 Hive定义

​ 定义:Hive是由FaceBook开源,主要用于解决海量结构化日志的数据统计。它是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射成一张表,并提供类SQL查询功能。

​ 本质上是将HSQL转化成MR程序。(具体来说是Hive内部封装了很多MapReduce模板,通过Hive框架匹配出相应的MapReduce模板直接运行MR程序)

​ Hive的数据存储是在HDFS,底层实现是MapReduce,执行程序是在Yarn上。

​ Hive加载数据到表中时不会做数据的校验,在读取数据时才校验。

hive底层支持的执行引擎有3种

  • mapreduce(默认)
  • tez(支持DAG作业的计算框架)
  • spark(基于内存的分布式计算框架),企业可以切换这种框架,基于内存的速度更快。

1.2Hive优缺点

  • 优点:
    • (1)操作接口采用类SQL语法,提供快速开发的能力(简单、容易上手)。
    • (2)避免了去写MapReduce,减少开发人员的学习成本。
    • (3)Hive支持用户自定义函数,用户可以根据自己的需求来实现自己的函数。
    • (4)底层实现是MapReduce,适合处理大数据。
  • 缺点:
    • (1)Hive 不支持记录级别的删改操作,主要原因是Hive在HDFS中存储,进行删除是物理删除,代价比较高,所以只支持覆盖和追加
    • (2)Hive 的查询延时很严重(主要浪费在MR程序的资源调度,任务计算)
    • (3)HSQL的表达能力有限:
      • Hive自动生成的MapReduce作业,通常情况下不够智能化
      • 数据挖掘方面不擅长,由于MapReduce数据处理流程的限制,效率更高的算法却无法实现。
    • (4)Hive调优比较困难,粒度较粗

二、Hive架构原理

E:\03-学习课程\大数据开发高级工程师\大数据开发高级工程师一期更新中\大数据高级开发1期代码及资料/20190809-hive/hive_day01/hive_day01课程设计.assets/

2.1 架构解释

  • 1、用户接口:Client
    • CLI(hive shell)、JDBC/ODBC(java访问hive)、WEBUI(浏览器访问hive)
  • 2、元数据:Metastore
    • 元数据包括:表名、表所属的数据库(默认是default)、表的拥有者、列/分区字段、表的类型(是否是外部表)、表的数据所在目录等;

      • 默认存储在自带的derby数据库中,有安装MySQL就可存储在Mysql中
  • 3、Hadoop集群
    • 使用HDFS进行存储,使用MapReduce进行计算。
  • 4、Driver:驱动器
    • 解析器(SQL Parser) 对SQL进行语法分析和语义分析。
      • 将SQL字符串转换成抽象语法树AST
      • 对AST进行语法分析,比如表是否存在、字段是否存在、SQL语义是否有误。
    • 编译器(Physical Plan):将AST编译生成逻辑执行计划。
    • 优化器(Query Optimizer):对逻辑执行计划进行优化。
    • 执行器(Execution):把逻辑执行计划转换成可以运行的物理计划。对于Hive来说默认就是mapreduce任务

2.2 Hive的工作原理(参考上面的架构图)

​ Hive首先是一个客户端工具,它提供了一些用户可操作的接口,可以通过交互shell,JDBC和web UI方式连接Hive,在Hive的内部有个Driver驱动器,驱动器里面实现了解析器,编译器,优化器和执行器的功能,在用Hsql查询表时,sql语句在驱动器中会先做语法和语义解析,解析之后再进行相应的语法编译生成逻辑执行计划,然后在通过优化器时对逻辑执行计划进行优化,最后在执行器中转换成对应的mr jar包,打包给hadoop集群运行获得结果。

总结:Hive 通过给用户提供的一系列交互接口,接收到用户的指令(SQL),使用自己的驱动器 Driver, 结合元数据(MetaStore),将这些指令翻译成 MapReduce,提交到 Hadoop 中执行,最后,将执行返回的结果输出到用户交互接口。

三、Hive与其他数据库比较

3.1 查询语言

​ Hive使用的是类SQL的查询语言,与标准SQL还是有一定区别。

3.2 数据存储位置

​ Hive的数据存储在HDFS中,而数据库则将数据保存在块设备或者本地文件系统中。

3.3 数据更新

​ Hive 不支持记录级别的删改操作,主要原因是Hive在HDFS中存储,进行删除是物理删除,代价比较高,所以只支持覆盖和追加。

​ 而数据库中的数据通常是需要经常修改的,支持数据更新操作。

3.4 索引

​ Hive在加载数据的过程中不会对数据进行任何处理,包括不会对数据中的某些key建立索引,在访问某些特定数据时,是扫描全表的数据,因此访问延迟较高。但hive底层是MR程序,可以并行访问数据,所以对于大数据量的情况下,hive查询仍然有优势。

​ 而数据库通常会针对一个或几个列建立索引,对于一些特定条件的查询具有很高的查询效率。

3.5 执行和执行延迟

​ Hive 中大多数查询的执行是通过 Hadoop 提供的 MapReduce 来实现的。而数据库通常有自己的执行引擎。

​ 前面说过hive没有索引,查询时是扫描整张表,所以延迟较高,另一个导致hive延迟高的原因在于底层是MR程序,时间都浪费在mr的资源调度(yarn分配资源)和任务计算中(map和reduce过程)。

​ 而数据库的执行延迟较低,但只适用数据规模较小的情况,当数据规模大到超过数据库的处理能力的时候,Hive 的并行计算显然能体现出优势。

3.6 可扩展性

​ Hive的扩展性和Hadoop一样好,可以增加机器提高性能。

​ 数据库由于ACID语义的限制,一般扩展很有限。

3.7 数据规模

​ Hive可以支持很大规模的数据;对应的,数据库支持的数据规模较小。

四、Hive安装

– 待补充

五、Hive常用命令

5.1 常用命令

# 退出
hive(default)>exit;

# 切换数据库
hive> use 数据库名;

# 查看数据库信息
hive> desc databases 数据库名;

# 显示数据库
hive> show databases;
# 过滤显示查询的数据库
hive> show databases like 'db_hive*';

# 查看系统自带的函数
hive> show functions;
# 然后显示自带的函数的用法
hive> desc function 函数名

# 查询表类型,可以查出很多表结构信息
hive>desc formatted 表名;

# 查看分区表有多少分区,注意加s
hive> show partitions 表名;

# 重命名表
hive> ALTER TABLE table_name RENAME TO new_table_name
# 更新列
ALTER TABLE table_name CHANGE [COLUMN] col_old_name col_new_name column_type [COMMENT col_comment] [FIRST|AFTER column_name];
# 添加列
alter table dept_partition add columns(deptdesc string);
# 替换列???
alter table student replace columns(deptno string,dname string,loc string); --替换表中所有的字段。

# 删除表
drop table dept_partition;

5.2 常用交互命令

前提是先启动Hadoop集群和mysql服务,并配置好hive元数据到mysql。

​ 交互方式有三种:Hive交互Shell,Hive JDBC服务和Hive的命令。

5.2.1 Hive交互shell

​ 通过输入在hive中的/bin目录下的hive命令,进入hive,直接输出sql查询.

$ cd 
$ bin/hive

mysql> show databases;
+--------------------+ | Database | +--------------------+ 
| information_schema | 
| metastore | 
| mysql | 
| performance_schema | 
| test | 
+--------------------+
5.2.2 Hive JDBC服务
  • 启动hiveserver2服务

    • 前台启动
    bin/hive --service hiveserver2
    
    • 后台启动
    nohup  bin/hive --service hiveserver2 &
    
    • 然后 启动beeline
    $ bin/beeline 
    Beeline version 1.2.1 by Apache Hive 
    beeline>
    
    # netstat nlp命令可以查看当前启动了哪些服务和对应的端口,用于下面连接用的端口10000
    
    • 最后beeline连接hiveserver2

      beeline> !connect jdbc:hive2://hadoop102:10000(回车)
      
5.2.3 Hive的命令方式
  • “-e”不进入 hive 的交互窗口执行 sql 语句
cd /opt/bigdata/hive
$ bin/hive -e "select id from student;"
  • “-f”执行脚本中 sql 语句
$ bin/hive -f /opt/module/datas/hivef.sql

5.3 其他命令操作

5.3.1 在 hive cli 命令窗口中如何查看 hdfs 文件系统
hive(default)>dfs -ls /;
5.3.2 在 hive cli 命令窗口中如何查看本地文件系统
hive(default)>! ls /opt/module/datas; 
5.3.3 查看在 hive 中输入的所有历史命令

(1)进入到当前用户的根目录/root 或/home/atguigu

(2)查看. hivehistory 文件

[atguigu@hadoop102 ~]$ cat .hivehistory

六、Hive数据类型

6.1 基本数据类型

Hive数据类型长度例子
tinyint1 byte有符号整数20
smalint2 byte有符号整数20
int4 byte有符号整数20
bigint8 byte有符号整数20
boolean布尔类型,true或falsetrue
float单精度浮点数3.14159
double双精度浮点数3.14159
string字符串,可以指定字符集‘now is the time’
timestamp时间类型1563157873
date日期20200829
binary字节数组

​ 对于 Hive 的 String 类型相当于数据库的 varchar 类型,该类型是一个可变的字符串,不过它不能声明其中最多能存储多少个字符,理论上它可以存储 2GB 的字符数。

6.2 集合数据类型

​ Hive 有三种复杂数据类型 ARRAY、MAP 和 STRUCT。ARRAY 和 MAP 与 Java 中的Array 和 Map 类似,而 STRUCT 与 C 语言中的 Struct 类似,它封装了一个命名字段集合。复杂数据类型允许任意层次的嵌套。

类型名称描述举例
array一组有序的字段,字段类型必须相同 array(元素1,元素2)Array(1,2,3)
map一组无序的键值对 map(k1,v1,k2,v2)Map(‘a’:1,‘b’:2)
struct一组命名的字段,字段类型可以不同 struct(元素1,元素2)Struct(‘a’,1,2,0)
  • array字段的元素访问方式:下标获取元素,下标从0开始

    • 获取第一个元素

      • array[0]
  • map字段的元素访问方式:通过键获取值

    • 获取a这个key对应的value

      • map[‘a’]
    #用法
    select map_key['a'] from user;
    
  • struct字段的元素获取方式:定义一个字段c的类型为struct{a int;b string},使用c.a 和c.b 获取其中的元素值

    • 这里可以把这种类型看成是一个对象

示例:

# 建表:
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';		--- 行分隔符

# 测试数据:
songsong,bingbing_lili,xiao song:18_xiaoxiao song:19,hui long guan_beijing yangyang,caicai_susu,xiao yang:18_xiaoxiao yang:19,chao yang_beijing

#导入数据后查询
hive (default)> select friends[1],children['xiao song'],address.city from test where name="songsong"; 
OK

_c0 _c1 city 
lili 18 beijing 

Time taken: 0.076 seconds, Fetched: 1 row(s)

6.3 类型转换

主要有两种,一种是隐式转换,小的会自动转换成大的,另一种是用cast函数手动转换

隐式类型转换

  • 系统自动实现类型转换,不需要用户干预

    • 如tinyint可以转换成int,int可以转换成bigint。

    • 所有整数类型、float 和 string类型都可以隐式地转换成double。

    • tinyint、smallint、int都可以转换为float。

    • boolean类型不可以转换为任何其它的类型。

手动类型转换

  • 可以使用cast函数操作显示进行数据类型转换

    • cast (‘1’ as int)将把字符串’1’ 转换成整数1;
    • 如果强制类型转换失败,如执行cast(‘x’ as int),表达式返回空值 NULL。

七、Hive需注意的DDL语句

7.1 数据库

  • 创建一个数据库,指定数据库在 HDFS 上存放的位置
    如果不指定,默认数据库存放的位置是/user/hive/warehouse/*.db。
create database if not exists 数据库名 location '/db_hive2.db';
  • 显示数据库信息
hive> desc database 数据库名;
  • 显示数据库详细信息
hive> desc database extended 数据库名;
  • 切换数据库
hive> use 数据库名;
  • 修改数据库:用户可以使用 ALTER DATABASE 命令为某个数据库的 DBPROPERTIES 设置键-值对属性值,来描述这个数据库的属性信息,但数据库的其他元数据信息都是不可更改的,包括数据库名和数据库所在的目录位置。
hive> alter database 数据库名 set dbproperties('createtime'='20170830');
  • 删除数据库

    如果数据库不存在,最好用if exists判断一下。

    另外也可以用cascade强制删除数据库

#删除数据库
hive>drop database if exists 数据库名
# 强制删除数据库
hive>drop database 数据库名 cascade;

7.2 建表语法

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]   row format delimited fields terminated by “分隔符”
[collection items terminated by ':'] (指定struct数据类型内的分隔符)
[map keys terminated by ':'] (指定map键值对的分隔符)
[STORED AS file_format] 
[LOCATION hdfs_path]
  • EXTERNAL 指定一个外部表
  • PARTITIONED BY 创建分区表
  • CLUSTERED BY 创建分桶
  • SORTED BY 按照字段排序(一般不常用)
  • ROW FORMAT 指定每一行中字段的分隔符
    • 比如:row format delimited fields terminated by ‘\t’
  • STORED AS 指定存储文件类型,
    • 常用的存储文件类型:SEQUENCEFILE(二进制序列文件)、TEXTFILE(文本)、RCFILE(列式存储格式文件)
    • 如果文件数据是纯文本,可以使用STORED AS TEXTFILE。如果数据需要压缩,使用 STORED AS SEQUENCEFILE。
  • LOCATION 指定表在HDFS上的存储位置。值得一提的是,假如建表后指定了location,后期直接往这个地址存储sql格式的数据,那么对应的表也是能查到这部分新增的数据的,因为这里建表定义的location实际上就是指向某个文件地址。

7.3 创建内部表和外部表

7.3.1 创建内部表-不加external

​ 建表语句不加external就是内部表。

​ 有三种常用建表方法:

  • 第一种,直接建表
create table if not exists student(
id int, 
name string
)
row format delimited fields terminated by '\t'	(如果不指定,整行数据会被当做一个字段)
stored as textfile;(不指定就默认是文本文件)
  • 第二种,查询建表法
#通过AS语句,将查询的子结果存在新表里
create table if not exists student1 as select id, name from student;
  • 第三种,Like建表法
create table if not exists student2 like student;
7.3.2 创建外部表
create external table if not exists default.emp(
id int,
name string,
age int
)
row format delimited fields terminated by '\t'
location '/hive/bigdata' (这个目录可以是不存在的,在建表时创建)
  • 创建外部表的时候需要加上external 关键字
  • location字段可以指定,也可以不指定
    • 指定就是数据存放的具体目录
    • 不指定就是使用默认目录 /user/hive/warehouse

在这里插入图片描述

7.4 内部表和外部表的区别

  • 1.创建表时:创建内部表时,会将数据移动到数据仓库指向的路径;创建外部表时需要加上external关键字,它仅记录数据所在的路径,不对数据的位置做任何改变
  • 2.删除表时:删除表后,内部表的元数据和真实数据会被一起删除,而外部表仅删除元数据,不删除真实数据,这样外部表相对来说更加安全些,数据组织也比较灵活,方便共享原始数据。(直接重建原来的表后,数据就自动导入到原来的表去了,location直接指向原来存储的位置.)
  • 外部表保障底层数据的安全性,内部表适用于管理中间表和结果表。

​ 内部表和外部表的互相转换:

#内部表改为外部表
alter table 表名 set tblproperties('EXTERNAL'='TRUE');

#外部表改为内部表
alter table 表名 set tblproperties('EXTERNAL'='FALSE');

7.5 Hive的分区表

​ 分区表实际上就是对应HDFS文件系统上的独立的文件夹。

​ Hive分区就是分目录,把表的数据分目录存储,存储在不同的文件夹下,后期按照不同的目录查询数据,不需要进行全量扫描,就可以提升查询效率。

​ 分区的层级没有上限,一般是3层。

7.5.1 分区表的基本操作
# 创建分区表语法
hive> create table dept_partition
( deptno int, dname string, loc string )
partitioned by (month string) 
row format delimited fields terminated by '\t';

# 加载数据到分区表中
hive> load data local inpath '/opt/module/datas/dept.txt' into table default.dept_partition partition(month='201709');

# 查询分区表中数据
hive> select * from dept_partition where month='201709';

#增加分区
hive> alter table dept_partition add partition(month='201706') ;
# 同时创建多个分区
hive> alter table dept_partition add partition(month='201705') partition(month='201704');

# 删除分区
hive> alter table dept_partition drop partition (month='201704');
# 同时删除多个分区
hive> alter table dept_partition drop partition (month='201705'), partition (month='201706');


7.5.2 创建二级分区
# 创建二级分区表
hive> create table dept_partition2
( deptno int, dname string, loc string )
partitioned by (month string, day string) 
row format delimited fields terminated by '\t';

# 加载数据到二级分区表中
hive> load data local inpath '/opt/module/datas/dept.txt' into table default.dept_partition2 partition(month='201709', day='13');
#也可以在HDFS创建这张表对应分区目录的存放位置,然后把数据放到该分区目录下,再创建分区时自动关联上这部分数据。
hive> dfs -mkdir -p /user/hive/warehouse/dept_partition2/month=201709/day=11;
hive> dfs -put /opt/module/datas/dept.txt /user/hive/warehouse/dept_partition2/month=201709/day=11;
hive> alter table dept_partition2 add partition(month='201709', day='11');
7.5.3 Hive的静态分区

​ insert 插入语句时,表的分区字段的值需要开发人员手动指定具体的值

  • 创建分区表

    create table order_partition(order_number string,
    order_price double,
    order_time string)
    partitioned by(month string)
    row format delimited fields terminated by '\t';
    
  • 导出数据到分区表

    load data local inpath '/opt/bigdata/order_created.txt' overwide into table order_partition partition(month='20200401');
    
7.5.4 Hive的动态分区

​ 按照需求实现把数据自动导入到表的不同分区中,不需要手动指定。

​ 注意要先开启动态分区的参数,以及分区字段要放在最后面。

#创建表语句没有变
create table order_partition(order_number string,
order_price double,
order_time string)
partitioned by(month string)
row format delimited fields terminated by '\t';

#不同的是导入时动态指定分区值
#首先需要设置动态分区的参数
hive>set hive.exec.dynamic.partition=true;//使用动态分区
hive>set hive.exec.dynamic.partition.mode=nonstrict //非严格模式

#然后导入数据,注意这里字段查询的顺序,分区字段一定要放在最后,否则数据有问题
insert into table order_partition partition(month) select order_number,order_price,order_time,month from 源表;

#最后可以查看分区,注意partition加s
show partitions order_partition;

7.6 Hive的分桶表

​ 分桶是将整个数据内容按照某列属性值去hash值进行区分,对取得的hash再做模运算(columnValue.hashCode % 桶数),具有相同结果的数据进入同一个文件中。(本质上来说可以看成是对一个大文件进行拆分成小文件)

​ 比如将name列分为4个桶,则name属性的值取hash值后对4求模运算,取模结果0,1,2,3的分开存放到不同文件中。

​ 可以将Hive中的分桶理解成MapReduce中的HashPartitioner的原理。

7.6.1 作用
  • 取样更高效,可以看下哪些值出现频率比较高,避免数据倾斜等。

    抽样查询桶表的语句:tablesample(bucket x out of y on column)

    x 表示从第几个桶开始取数

    y 表示每隔几个桶取一个分桶

    select * from user_buckets_demo tablesample(bucket 1 out of 2 on id); 
    --假设建表时分了4个桶。
    --表示从第一个桶开始抽样,每隔2个桶取一个桶抽样,第二个桶是1+2 = 3
    --需要抽样的桶数 : 4/2 = 2个
    
  • 提升某些查询操作效率,比如map-side join(在对列分桶时,已经将相同值聚集在一起了,所以能提高查询效率)

7.6.2 案例
create table user_buckets_demo(id int,name string)
clustered by(id) into 4 buckets
row format delimited fields terminated by '\t';

# 注意,建完分桶表之后,要开启一下参数,否则实际hdfs上的文件是不会被拆分的。
hive (default)> set hive.enforce.bucketing=true; 
hive (default)> set mapreduce.job.reduces=-1;

#加载数据到分桶表
#分桶表不能直接load加载数据,需要从其它表查询插入
Insert into table user_buckets_demo select * from user_demo;
7.6.3 分桶表和分区表的区别
  • 在建表语句中,分桶表必须是建表中已有的字段,而分区表必须是建表中没有的字段。
  • 分区有动态分区和静态分区,而分桶没有,但分桶的数量可以固定。
  • 分区针对的是数据的存储路径;分桶针对的是数据文件。
    • 分区就是分目录,把表的数据分目录存储,存储在不同的文件夹下,后期按照不同的目录查询数据,不需要进行全量扫描,就可以提升查询效率;而分桶是对原始数据的某个字段取哈希值求模运算后,将相同结果存入同一文件夹,相当于把大文件拆分成多个小文件,由于相同值已经聚集在一起,更适合用于map-side join和抽样取数。

八、Hive需注意的DML语句

8.1 Hive数据的导入

8.1.1 向表中装载数据(Load)

语法:

load data [local]  inpath  'dataPath'  override | into table student [partition 分区值];

​ load data:表示加载数据

​ local:表示从本地加载数据到表,如果忽略不写则表示从HDFS加载数据

​ inpath:表示加载数据的路径

​ override:表示覆盖表中已有的数据,需要注意使用

​ table:表示加载到哪张表

​ partition:表示上传到制定分区

#例如:
load data local inpath '/opt/bigdata/person.txt' into table person partition(dt='20200401');
8.1.2 通过查询语句向表中插入数据(Insert)
  • 从指定的表中查询数据结果然后插入目标表中
insert into | override table student select * from XXXX;

insert into | override table student partition(dt='20200401') select * from XXXX;
8.1.3 查询语句中创建表并导入数据(AS Select)
create table if not exists student as select * from XXXX;
8.1.4 创建表时通过location指定加载数据路径
create table if not exists student (id int,
name string)
row format delimited fields terminated by '\t'
location '/user/hive/warehouse/student1';

#然后往hdfs的路径上传对应的数据student.txt
dfs -put /opt/bigdata/student.txt /user/hive/warehouse/student1
8.1.5 import数据到指定hive表中

​ 注意先用export导出数据后,再import

create table student2 like student1;

export table student1 to '/opt/bigdata/student1';(这里是hdfs的目录)
import table student2 from '/opt/bigdata/student1';

8.2 Hive数据的导出

8.2.1 Insert 导出
  • 1.将查询的结果导出到本地
insert override local directory '/opt/bigdata/student';
#默认文件分隔符会是‘\001’  	
#之后本地会生成一个日志型的文件
  • 2.将查询的结果格式化后导出到本地
insert override local directory '/opt/bigdata/student' row format delimited fields terminated by ',';
  • 3.将查询的结果导出到HDFS(注意这里没有local)
insert override directory '/export/student' row format delimited fields terminated by ',';
8.2.2 Hadoop命令导出

​ get命令直接下载到本地磁盘

dfs -get /usr/hive/warehouse/student/student.txt /opt/bigdata/data
8.2.3 Hive shell命令导出

​ 有两种:

  • 1.hive -e “sql语句” >> file; 这种是直接执行sql语句,把结果导出到文件中。

  • 2.hive -f “sql文件” > file; 这种是执行完sql文件后,将查询结果写入到file中

bin/hive -e 'select * from default.student;' >> /opt/bigdata/student/student.txt
8.2.4 export导出到HDFS上
hive>export table student to '/usr/hive/warehouse/student';

8.3 清除表中的数据truncate

​ 注意:Truncate 只能删除内部表,不能删除外部表中数据

hive (default)> truncate table student;

九、Hive查询注意点

9.1 连接查询

  • 笛卡尔积
    • 定义:
    • 条件:省略连接条件、连接条件无效、所有表中的所有行互相连接
  • 内连接:返回连接的两个表中与连接条件相匹配的数据
  • 左外连接:返回JOIN 操作符左边表中符合 WHERE 子句的所有记录,右表不符合的显示为空。
  • 右外连接:返回JOIN 操作符右边表中符合 WHERE 子句的所有记录,左表不符合的显示为空。
  • 全外连接:返回所有表中符合 WHERE 语句条件的所有记录。如果任一表的指定字段没有符合条件的值的话,那么就使用 NULL 值替代。

9.2 开启本地模式

​ 如果没有开启本地模式,那么大部分函数操作会经过yarn调度分配,速度较慢。开启后就不会提交到yarn中去,能快速返回查询结果。

​ 限制:如果文件超过256M或者文件个数超过4个,那么系统也会自动关闭本地模式。

set hive.exec.model.local.auto=true;

9.3 排序

9.3.1 order by 全局排序

​ Order By:全局排序,在最后进行排序时只有一个reduce

9.3.2 sort by内部排序

​ Sort By:每个 Reducer 内部进行排序,对全局结果集来说不是排序。

​ sort by针对多个reduce中每个reduce进行排序。

  • 设置reduce个数
set mapreduce.job.reduces=3;
  • 查看reduce个数
set mapreduce.job.reduces;
  • 将查询结果导入到文件中(按照成绩列降序排序)
insert override local directory'/opt/bigdata/sort' select * from student s sort by s.score;

#查询结果会被分成3个文件,每个文件都按成绩排序好。
9.3.3 distribute by 分区排序

​ Distribute By:类似于mr中的partition,采用hash算法,在map端将查询结果中hash值相同的结果分发到对应的reduce中,结合sort by使用。

​ 注意,Hive 要求 DISTRIBUTE BY 语句要写在 SORT BY 语句之前。

​ 字段.hashCode % reduce个数

​ 案例:

#先设置reduce个数
set mapreduce.job.reduces=3;

#通过distribute by 进行数据的分区,将不同的sid划分到不同的reduce中去
insert override local directory '/opt/bigdata/distribute' select * from student distribute by sid sort by score;

#结果是先根据sid分到不同reduce,然后在每个reduce对成绩排序,那么如果这里reduce只有一个,其实跟全局排序效果是一样的

​ 有一点要注意的是,distribute by和分桶的概念很像,都用到了hashcode和模运算,不同的是分桶是为了在hdfs存储时将大文件分成多个内部相关联的小文件,而distribute by 这里是针对查询的结果集进行一个排序展示。

9.3.4 cluster by

​ 当distribute by 和sort by的字段一样时,可以用cluster by代替,效果是一样的

​ 但是cluster by排序只能是升序排序,不能指定排序规则为 ASC 或者 DESC。

9.4 行转列

​ 行转列主要用到这三个函数:

  • CONCAT(string A/col, string B/col…):拼接字符串,只要其中一个是 NULL,那么将返回 NULL
  • CONCAT_WS(separator, str1, str2,):指定分隔符进行拼接字符串,只要有一个字符串不是 NULL,就不会返回 NULL。
  • COLLECT_SET(col):相当于把某个字段的多行数据编成一行,并且如果多行里面有重复值,那它结果也会去重。

案例:

​ 现有这样一张表

在这里插入图片描述

需求:
在这里插入图片描述

SQL语句:

select t1.base, 
		concat_ws('|', collect_set(t1.name)) name 
from(select name, 
			concat(constellation, ",", blood_type) base 
			from person_info
	) t1 group by t1.base;

9.5 列转行

​ 可以使用lateral view函数

​ 函数说明

​ EXPLODE(col):将 hive 一列中复杂的 array 或者 map 结构拆分成多行。

​ LATERAL VIEW

​ 用法:LATERAL VIEW udtf(expression) tableAlias AS columnAlias

​ 解释:用于和 split, explode 等 UDTF 一起使用,它能够将一列数据拆成多行数据,在此基础上可以对拆分后的数据进行聚合。

原数据:

在这里插入图片描述

需求:

在这里插入图片描述

select movie, category_name 
from movie_info 
lateral view explode(category) table_tmp as category_name;

十、Hive常用函数

10.1 concat函数

concat 函数在连接字符串的时候,只要其中一个是 NULL,那么将返回 NULL

hive> select concat('a','b'); --ab
hive> select concat('a','b',null); --NULL 

10.2 concat_ws 函数

concat_ws 函数在连接字符串的时候,只要有一个字符串不是 NULL,就不会返回 NULL。

concat_ws 函数需要指定分隔符。

ab可以换成数组,结果是对数组内的每一个进行拼接。

hive> select concat_ws('-','a','b'); --a-b 
hive> select concat_ws('-','a','b',null); --a-b 
hive> select concat_ws('','a','b',null); --ab

10.3 STR_TO_MAP 函数

也就是将字符串变成键值对。

(1)语法描述

​ STR_TO_MAP(VARCHAR text, VARCHAR listDelimiter, VARCHAR keyValueDelimiter)

(2)功能描述

​ 使用 listDelimiter 将 text 分隔成 K-V 对,然后使用 keyValueDelimiter 分隔每个 K-V 对,

​ 组装成 MAP 返回。默认 listDelimiter 为( ,),keyValueDelimiter 为(=)。

(3)案例

str_to_map('1001=2020-03-10,1002=2020-03-10', ',' , '=') 
输出
{"1001":"2020-03-10","1002":"2020-03-10"}

10.4 Collect_set函数

​ 相当于把某个字段的多行数据编成一行,并且如果多行里面有重复值,那它结果也会去重。

​ 要和group by结合使用。

​ 举例:

#表中有这样的数据:
hive (gmall)> select * from stud;
stud.name stud.area stud.course stud.score
zhang3 		bj 			math 		88
li4 		bj 			math 		99
wang5 		sh 			chinese 	92
zhao6 		sh 			chinese 	54
tian7 		bj 			chinese 	91

#把同一分组的不同行的数据聚合成一个集合
hive (gmall)> select course, collect_set(area), avg(score) from stud group by course;
chinese ["sh","bj"] 79.0 
math 	["bj"] 		93.5

#用下标可以取某一个
hive (gmall)> select course, collect_set(area)[0], avg(score) from stud group by course;
chinese sh 79.0 
math 	bj 93.5

10.5 nvl函数

​ NVL(表达式 1,表达式 2)

​ 返回第一个不为空的表达式。该函 数的目的是把一个空值(null)转换成一个实际的值。其表达式的值可以是数字型、字符型 和日期型。但是表达式 1 和表达式 2 的数据类型必须为同一个类型。

10.6 日期函数

  • date_format函数

​ 相当于to_date函数

  • date_add 函数(加减日期)

  • next_day 函数

    取下一个…的日期,比如

#取当前天的下一个周一
hive (gmall)> select next_day('2020-03-12','MO'); --2020-03-16

#取当前周的周一
hive (gmall)> select date_add(next_day('2020-03-12','MO'),-7);
  • last_day 函数(求当月最后一天日期)
hive (gmall)> select last_day('2020-03-10'); --2020-03-31

10.7 LATERAL VIEW函数

函数说明 :

​ EXPLODE(col):将 hive 一列中复杂的 array 或者 map 结构拆分成多行。

LATERAL VIEW

用法:LATERAL VIEW udtf(expression) tableAlias AS columnAlias

解释:用于和 split, explode 等 UDTF 一起使用,它能够将一列数据拆成多行数据,在此基础上可以对拆分后的数据进行聚合。

原数据:

在这里插入图片描述

需求:
在这里插入图片描述

select movie, category_name 
from movie_info 
lateral view explode(category) table_tmp as category_name;

十一、Hive压缩和存储

11.1开启Map端输出阶段压缩

​ 由于Hive底层是运行Mr程序,所以Hive的压缩配置和前面MapReduce的数据压缩是一样的。

​ 开启 map 输出阶段压缩可以减少 job 中 map 和 Reduce task 间数据传输量。具体配置如下:

# 开启 hive 中间传输数据压缩功能
hive (default)>set hive.exec.compress.intermediate=true;

# 开启 mapreduce 中 map 输出压缩功能
hive (default)>set mapreduce.map.output.compress=true;

# 设置 mapreduce 中 map 输出数据的压缩方式:Snappy
hive (default)>set mapreduce.map.output.compress.codec= org.apache.hadoop.io.compress.SnappyCodec;

11.2 开启Reduce端输出阶段压缩

​ 当 Hive 将 输 出 写 入 到 表 中 时 , 输 出 内 容 同 样 可 以 进 行 压 缩 。 属 性 hive.exec.compress.output 控制着这个功能。用户可能需要保持默认设置文件中的默认值 false,这样默认的输出就是非压缩的纯文本文件了。用户可以通过在查询语句或执行脚本中设置这个值为 true,来开启输出结果压缩功能。

# 开启 hive 最终输出数据压缩功能
hive (default)>set hive.exec.compress.output=true;

# 开启 mapreduce 最终输出数据压缩
hive (default)>set mapreduce.output.fileoutputformat.compress=true;

# 设置 mapreduce 最终数据输出压缩方式
hive (default)> set mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;

# 设置 mapreduce 最终数据输出压缩为块压缩
hive (default)> set mapreduce.output.fileoutputformat.compress.type=BLOCK;

11.3 文件存储格式

​ Hive 支持的存储数的格式主要有:TEXTFILE 、SEQUENCEFILE、ORC、PARQUET。

11.3.1 列式存储和行式存储

在这里插入图片描述

​ 如图 6-10 所示左边为逻辑表,右边第一个为行式存储,第二个为列式存储。

  • 1.行存储的特点

    查询满足条件的一整行数据的时候,列存储则需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。(注意是一整行)

  • 2.列存储的特点

    因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量;每个字段的数据类型一定是相同的,列式存储可以针对性的设计更好的设计压缩算法。(注意是几个字段,列式存储在读取时,都是直接读取这几列下的数据,而行式存储读取几列时,是在一行上重复找符合的字段数据)

​ TEXTFILE 和 SEQUENCEFILE 的存储格式都是基于行存储的;

​ ORC 和 PARQUET 是基于列式存储的。

11.3.2 TextFile格式

​ 默认格式,数据不做压缩,磁盘开销大,数据解析开销大。可结合 Gzip、Bzip2 使用,但使用 Gzip 这种方式,hive 不会对数据进行切分,从而无法对数据进行并行操作。 (如果可以分割,那么单一文件可以由多个mapreduce程序处理,可以更好的并行化)

11.3.3 Orc格式

– 待补充

11.3.4 Parquet 格式

– 待补充

11.3.5 主流文件存储格式压缩对比

– 待补充

参考《Hive主流文件存储格式对比.md》 https://blog.csdn.net/xsdxs/article/details/53152599

​ 压缩对比过程:

# 创建表,存储数据格式为 TEXTFILE
create table log_text 
( track_time string, url string, session_id string, referer string, ip string, end_user_id string, city_id string )
row format delimited fields terminated by '\t' 
stored as textfile ;
# 向表中加载数据
hive (default)> load data local inpath '/opt/module/datas/log.data' into table log_text ;
# 查看表中数据大小
hive (default)> dfs -du -h /user/hive/warehouse/log_text;
--18M

# 创建表,存储数据格式为 ORC
create table log_orc
( track_time string, url string, session_id string, referer string, ip string,end_user_id string, city_id string )
row format delimited fields terminated by '\t' 
stored as orc ;
# 向表中加载数据
hive (default)> insert into table log_orc select * from log_text ;
# 查看表中数据大小
hive (default)> dfs -du -h /user/hive/warehouse/log_orc/ ;
--2.8M

# 创建表,存储数据格式为 parquet
create table log_parquet
( track_time string, url string, session_id string, referer string, ip string, end_user_id string, city_id string )
row format delimited fields terminated by '\t' 
stored as parquet ;
# 向表中加载数据
hive (default)> insert into table log_parquet select * from log_text ;
# 查看表中数据大小
hive (default)> dfs -du -h /user/hive/warehouse/log_parquet/ ;
--13.1M

​ 存储文件的压缩比总结:

​ ORC > Parquet > textFile

​ 注意,除了textFile外的其他三种是不能直接从本地文件导入到hdfs的,数据需要先导入到textFile格式的表中,然后再通过textFile的表用insert导入到他们自身对应格式的表中。

hive (default)> select count(*) from log_text;
hive (default)> select count(*) from log_orc;
hive (default)> select count(*) from log_parquet;

​ 存储文件的查询速度总结:查询速度相近。

11.4 存储和压缩结合

11.4.1 修改Hadoop 集群具有 Snappy 压缩方式
# 查看Hadoop支持的压缩方式
[atguigu@hadoop104 hadoop-2.7.2]$ hadoop checknative

​ hadoop默认是不支持snappy压缩的,需要自行安装配置。

# 将编译好的支持 Snappy 压缩的 hadoop-2.7.2.tar.gz 包导入到 hadoop102 的 /opt/software 中
# 然后解压
[atguigu@hadoop102 software]$ tar -zxvf hadoop-2.7.2.tar.gz

# 进入到/opt/software/hadoop-2.7.2/lib/native 路径可以看到支持 Snappy 压缩的 动态链接库
[atguigu@hadoop102 native]$ pwd 
/opt/software/hadoop-2.7.2/lib/native 
[atguigu@hadoop102 native]$ ll
-rw-r--r--. 1 atguigu atguigu 472950 9 月 1 10:19 libsnappy.a 
-rwxr-xr-x. 1 atguigu atguigu 955 9 月 1 10:19 libsnappy.la 
lrwxrwxrwx. 1 atguigu atguigu 18 12 月 24 20:39 libsnappy.so -> libsnappy.so.1.3.0 lrwxrwxrwx. 1 atguigu atguigu 18 12 月 24 20:39 libsnappy.so.1 -> libsnappy.so.1.3.0 
-rwxr-xr-x. 1 atguigu atguigu 228177 9 月 1 10:19 libsnappy.so.1.3.0

# 拷贝/opt/software/hadoop-2.7.2/lib/native 里面的 所有内容到 开发集群的 /opt/module/hadoop-2.7.2/lib/native 路径上(还是102)
[atguigu@hadoop102 native]$ cp ../native/* /opt/module/hadoop-2.7.2/lib/native/
# 分发集群
[atguigu@hadoop102 lib]$ xsync native/
# 再次查看就可以看到支持了。
[atguigu@hadoop102 hadoop-2.7.2]$ hadoop checknative

​ 注意,最后要重新启动 hadoop 集群和 hive才会生效。

11.4.2 测试存储和压缩

官网:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+ORC

ORC 存储方式的压缩:

在这里插入图片描述

​ 这里我们用一个非压缩的Orc存储和一个采用Snappy压缩的Orc存储做对比:

# 创建一个非压缩的的 ORC 存储方式
create table log_orc_none
( track_time string, url string, session_id string, referer string, ip string, end_user_id string, city_id string )
row format delimited fields terminated by '\t' 
stored as orc tblproperties ("orc.compress"="NONE");

hive (default)> insert into table log_orc_none select * from log_text ;
# 查看插入后数据
hive (default)> dfs -du -h /user/hive/warehouse/log_orc_none/ ;
--7.7M

# 创建一个 SNAPPY 压缩的 ORC 存储方式
create table log_orc_none
( track_time string, url string, session_id string, referer string, ip string, end_user_id string, city_id string )
row format delimited fields terminated by '\t' 
stored as orc tblproperties ("orc.compress"="SNAPPY");

hive (default)> insert into table log_orc_snappy select * from log_text ;
# 查看插入后数据
hive (default)> dfs -du -h /user/hive/warehouse/log_orc_snappy/ ;
--3.8 M

# 上一节中默认创建的 ORC 存储方式,导入数据后的大小为2.8M,比用snappy的还要小
#这是因为orc默认使用的就是zlib压缩

​ 总结:虽然orc默认的zlib压缩方式比snappy还小,但snappy的压缩和解压缩效率比较高,所以压缩方式一般选择snappy。在实际的项目开发当中,hive 表的数据存储格式一般选择:orc 或 parquet。压缩方式一

般选择 snappy,lzo。

十二、Hive企业级调优

12.1 Fetch抓取

​ Fetch抓取是指Hive中对某些查询可以不必使用mapreduce计算。

​ 比如select * from

​ 在这种情况下,Hive可以简单地读取表对应存储目录下的文件,然后输出查询结果到控制台。

​ 在配置文件hive-default.xml.template文件中,默认设置hive.fetch.task.conversion=more,这样在全局查找,字段查找和limit查找等都不走mapreduce。

set hive.fetch.task.conversion=more;
#直接返回,不走mapreduce
select * from t1;
select sex from t1;
select * from t1 limit5;

12.2 本地模式

​ Hive默认情况下是启用hadoop的job模式,把任务提交到集群中运行,但我们可以通过设置本地模式让hive在单台机器上处理任务,对于小数据集,执行时间可以明显被缩短。

#开启本地模式,并执行查询语句、
set hive.exec.mode.local.auto=true;

#设置本地模式的最大输入数据量,当输入数据量小于这个值时就采用本地模式,默认为128M
set hive.exec.mode.local.auto.inputbytes.max=50000000;

#设置本地模式的最大输入文件个数,当输入文件个数小于这个数字时就启用本地模式,默认为4
set hive.exec.mode.local.auto.input.files.max=10;

12.3 表的优化

12.3.1 小表、大表 Join

​ 可以将 key 相对分散,并且数据量小的表放在 join 的左边,这样可以有效减少内存溢出错误发生的几率;

​ 再进一步,可以先让小表(1000条记录以下的)进入到内存中,后续大表加载时可以直接从内存中获取小表的数据,整个过程都在map端执行,不需要用到reduce,也就不需要用到mr中的shuffle,能提升查询效率。

​ map join就是让多个数据结果直接在map端完成聚合操作,也就是在map端实现reduce端的逻辑。

12.3.2 大表join大表

​ 可能遇到查询慢的情况处理:

  • 空key过滤

    有时 join 超时是因为某些 key 对应的数据太多,而相同 key 对应的数据都会发送到相同 的 reducer 上,从而导致内存不够。此时我们应该仔细分析这些异常的 key,很多情况下, 这些 key 对应的数据是异常数据,我们需要在 SQL 语句中进行过滤。例如 key 对应的字段 为空,

# 在语句中排除空值
hive (default)> insert overwrite table jointable 
select n.* from 
(select * from nullidtable where id is not null 
) n left join ori o on n.id = o.id;
  • 空key转换

    有时虽然某个 key 为空对应的数据很多,但是相应的数据不是异常数据,必须要包含在join 的结果中,此时我们可以表 a 中 key 为空的字段赋一个随机的值,使得数据随机均匀地分不到不同的 reducer 上。例如:

insert overwrite table jointable 
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;

​ 这里是假设连接字段为空比较多,直接用拼接字符串去给他随机赋值,这样就能平均分配到不同reduce执行。

12.3.3 MapJoin

​ 如果不指定map join或者不符合map join的条件,那么Hive解析器会将join操作转换成common join,即在reduce阶段完成join,容易发生数据倾斜,可以用map join把小表全部加载到内存,在map端进行join,避免reduce处理。

  • 工作机制:简单来说就是先将小表存入内存中,然后加载到分布式缓存,后面大表会另外启动一个没有reduce的task,并在map阶段和刚刚存储在分布式缓存中的小表关联合并,然后输出结果文件。有多少个map task 就有多少个结果文件。
insert overwrite table jointable 
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;
12.3.4 设置hive的group by参数

​ 默认情况下,Map 阶段同一 Key 数据分发给一个 reduce,当一个 key 数据过大时就倾斜了。

​ 并不是所有的聚合操作都需要在reducec端完成,很多聚合操作都可以现在map端先进行部分聚合,最后在reduce端完成最终的聚合操作。

​ 案例:

# 是否在map段進行聚合,默认是true
set hive.map.aggr = true
# 在map端进行聚合操作的条目数目
set hive.groupby.mapaggr.checkinterval = 100000;
# 有数据倾斜的时候进行负载均衡,比如把相同特征的key分发到不同的reduce中(默认是false)
set hive.groupby.skewindata = true;

​ 当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中对应负载均衡的设置,Map 的输 出结果会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第 二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以 保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。

12.3.5 Count(distinct)去重统计

​ 数据量小的时候无所谓,数据量大的情况下,由于 COUNT DISTINCT 操作需要用一个Reduce Task 来完成,这一个 Reduce 需要处理的数据量太大,就会导致整个 Job 很难完成,一般 COUNT DISTINCT 使用先 GROUP BY 再 COUNT 的方式替换:

# 每个reduce任务处理的数据量默认是256M
# 这个设置其实可以不用改。
set hive.exec.reducers.bytes.per.reducer= 32123456;

select count(distinct id) from log_text;
# 改成:
select count(id) from (select id from log_text group by id);

# 虽然可能会多开一个job来运行group by,但换取的查询速度是值得的。
12.3.6 避免笛卡尔积

​ 尽量避免笛卡尔积,产生笛卡尔积时,hive只能使用一个reduce来做相应处理,那么等待时间就变很长。

12.3.7 行列过滤

​ 列处理:在 SELECT 中,只拿需要的列,如果有,尽量使用分区过滤,少用 SELECT *。

​ 行处理:在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在 Where 后面,那么就会先全表关联,之后再过滤,所以建议副表先过滤后关联。

​ 比如:

select o.id from bigtable b join ori o on o.id = b.id where o.id <= 10;
--Time taken: 34.406 seconds, Fetched: 100 row(s)

select b.id from bigtable b join (select id from ori where id <= 10 ) o on b.id = o.id;
--Time taken: 30.058 seconds, Fetched: 100 row(s)
12.3.8 动态分区调整

​ Hive使用动态分区前,需要先进行配置。

# 开启动态分区,默认是开启的
set hive.exec.dynamic.partition=true
# 设置为非严格模式,动态分区的模式,默认 strict,表示必须指定至少一个分区为 静态分区,nonstrict 模式表示允许所有的分区字段都可以使用动态分区
set hive.exec.dynamic.partition.mode=nonstrict

# 在所有执行 MR 的节点上,最大一共可以创建多少个动态分区。
set hive.exec.max.dynamic.partitions=1000
# 在每个执行 MR 的节点上,最大可以创建多少个动态分区。(这里是每个MR,上面是一共能创建多少个)
# 该参数需要根据实际 的数据来设定。比如:源数据中包含了一年的数据,即 day 字段有 365 个值,那么该参数就 需要设置成大于 365,如果使用默认值 100,则会报错。
set hive.exec.max.dynamic.partitions.pernode=100

# 整个 MR Job 中,最大可以创建多少个 HDFS 文件。
set hive.exec.max.created.files=100000
# 当有空分区生成时,是否抛出异常。一般不需要设置。
set hive.error.on.empty.partition=false
12.3.9 使用分区或分桶

​ 分区参见Hive 七-7.5 Hive的分区表。

​ 分桶参见Hive 七-7.6 Hive的分桶表。

12.4 数据倾斜问题

​ hive针对数据倾斜,还有以下方法可以解决

12.4.1 合理设置Map数
  • 通常情况下map数是根据输入文件总格数,文件大小和集群设置的文件块大小决定的,如果一个任务中有很多小文件,每个文件都当作一个块需要一个map任务来运行,那么此时不是越多map数越好,map任务的启动和初始化过程也会占用时间,这时就应该考虑减少map数
  • 另外一种情况,假如某个map它处理的逻辑比较负责,比如某个文件有几千万的记录,那么只交给一个map处理的话明显耗费的时间很长,这时可以考虑增加map数
12.4.2 在map执行之前将小文件合并可以减少map数
  • 在map执行前合并小文件,减少map数

    • CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)
    set hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
    
12.4.3 对复杂文件可以增加map数
  • 当输入的文件都很大,任务逻辑复杂,map执行非常缓慢的时候可以设当增加map数,来使得每个map处理的数据量减少,从而提高速度

  • 增加map方法为:

    • 根据computeSliteSize(Math.max(minSize,Math.min(maxSize,blocksizs))) = blocksize = 128M公式
    • 调整maxSize最大值,让maxSize小于blocksize就可以增加map数
    mapreduce.input.fileinputformat.split.minsize = 1;# 默认值为1
    
    mapreduce.input.fileinputforma.split.maxsize = long.MAXValue;#默认值
    
    #有这样一个公式:Math.max(minSize,Math.min(maxSize,blockSize))
    
    #比如这样设置就可以达到增加map数的效果
    #设置maxSize大小为10M,这样小于一个block128M的话,系统就会分配更多10M的map来处理复杂任务。
    set mapreduce.input.fileinputformat.split.maxsize = 10485760;
    
12.4.4 合理设置Reduce数
  • 1.调整reduce个数的方法一:

    • 设置每个reduce处理的数据量,默认是256M
    set hive.exec.reducers.bytes.per.reducer = 256000000;
    
    • 设置每个任务最大的reduce数,默认是1009
    set hive.exec.reducers.max = 1009;
    

    这样reduce的个数就处于这样一个范围值:N = (总输入数据量 / 每个reduce处理的数据量,每个任务最大的reduce数)

  • 2.调整reduce个数的方法二:

    • 设置每个job中reduce的个数,默认是1
    set mapreduce.job.reduces = 3;
    

​ 注意,但是reduce也不是越多越好的,过多的启动和初始化 reduce 也会消耗时间和资源;另外,有多少个reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;

12.5 并行执行

​ 和oracle一样也可以利用并行执行提高查询速度,不同的是hive是靠参数来控制的,一定要在系统资源空闲时使用。

#开启并行执行
set hive.exec.parallel = true;
#设置同一个sql允许的最大并行度,默认是8
set hive.exec.parallel.thread.number = 16;

12.6 严格模式

​ hive有可以设置严格模式,可以防止用户执行那些可能意想不到的,或者有不好影响的查询,比如等待了长时间发现是笛卡尔积,或者查询大表时忘记加分区条件。

set hive.mapred.mode = strict;

​ 开启严格模式后,就可以禁止以下三种操作:

  • (1)对于分区表,在where条件中必须要有分区字段的过滤条件来限制范围;

  • (2)对于使用了order by的查询,要求使用limit语句

  • (3)笛卡尔积;

    违反以上三种情况,系统都会直接报错,不允许执行。

12.7 JVM重用

​ JVM重用可以使得JVM实例在同一个job中重新使用多次,减少进程的启动和销毁时间。

​ JVM 重用是 Hadoop 调优参数的内容,其对 Hive 的性能具有非常大的影响,特别是对于很难避免小文件的场景或 task 特别多的场景,这类场景大多数执行时间都很短。

​ Hadoop 的默认配置通常是使用派生 JVM 来执行 map 和 Reduce 任务的。这时 JVM 的启动过程可能会造成相当大的开销,尤其是执行的 job 包含有成百上千 task 任务的情况。JVM重用可以使得 JVM 实例在同一个 job 中重新使用 N 次。N 的值可以在 Hadoop 的mapred-site.xml 文件中进行配置。通常在 10-20 之间,具体多少需要根据具体业务场景测试

得出。

#设置jvm重用个数
set mapred.job.reuse.jvm.num.tasks = 5;

​ 这个功能的缺点是,开启 JVM 重用将一直占用使用到的 task 插槽,以便进行重用,直到任务完成后才能释放。如果某个“不平衡的”job 中有某几个 reduce task 执行的时间要比其 他 Reduce task 消耗的时间多的多的话,那么保留的插槽就会一直空闲着却无法被其他的 job使用,直到所有的 task 都结束了才会释放。

12.8 推测执行

​ Hadoop采用了推测执行机制,它根据一定的法则推测出”拖后腿“的任务,并为这样的任务启动一个备份任务,让备份任务和原始任务同时处理一份数据,并最后选择优先执行完成的任务计算结果作为最终结果。

#开启推测执行机制
set hive.mapred.reduce.tasks.speculative.exection = true;

​ 注意,如果用户因为输入数据量很大而需要执行长时间的 map 或者 Reduce task 的话,那么启动推测执行造成的浪费是非常巨大大。

12.9 使用压缩

​ 详见Hive-十一、Hive压缩和存储

  • Hive表中间数据压缩
#设置为true为激活中间数据压缩功能,默认是false,没有开启
set hive.exec.compress.intermediate = true;
#设置中间数据的压缩算法
set mapred map.output.compression = codec = org.apache.hadoop.io.compress.SnappyCodec;
  • Hive表最终输出结果压缩
set hive.exec.compress.output = true;
set mapred map.output.compression = codec = org.apache.hadoop.io.compress.SnappyCodec;

12.10 查看执行计划

1.基本语法

​ EXPLAIN [EXTENDED | DEPENDENCY | AUTHORIZATION] query

2.案例实操

(1)查看下面这条语句的执行计划

hive (default)> explain select * from emp; 
hive (default)> explain select deptno, avg(sal) avg_sal from emp group by deptno; 

(2)查看详细执行计划

hive (default)> explain extended select * from emp; 
hive (default)> explain extended select deptno, avg(sal) avg_sal from emp group by 
deptno;

Hive课后题

1. 将数据直接传到HDFS分区目录上,怎么让分区表和数据产生关联?

因为上传到hdfs后,hive没有对应元数据信息所以无法查询到对应数据。可以上传数据后给分区表添加该目录的分区

dfs -mkdir -p 分区目录
dfs -put 分区数据
hive>alter table 表明 add partition(分区);
2. 桶表是否可以直接通过load将数据导入?

不可以,因为load数据的话hdfs下只会有一个文件无法完成分桶的效果,需要通过中间表导入数据

3. hive的分区可以提高效率,那么分区是否越多越好?为什么?

不是越多越好

  • hive底层是存储在hdfs上的,hdfs是适合存储大文件而不适合小文件,如果有越多的分区,那么会增加namenode的负担。
  • hive会转化成mr程序,mr会转化为多个task任务,多个个小文件的话,每个文件一个task,每个task运行一个JVM实例,JVM的开启和销毁都会降低系统性能。

所以分区数要合理设计,一般在3个以内。

4. 什么情况下Hive可以避免进行mapreduce?
  • 如果是进行简单的查询,直接select,不带count,sum这些聚合函数的,都不会走mapreduce,而是直接读取hdfs目录中的文件。
  • 另外如果查询语句中的过滤条件只是分区字段的情况下,也不会走mapreduce
select * from order_partition where month = '2019-03';
  • 还有就是可以手动设置,让hive使用本地模式,当然这种有限制,需要查询的文件不超过256M或者文件数量不超过4个,否则系统还是会自动走mapreduce
set hive.exec.mode.local.auto = true;
5. order by ,sort by , distribute by , cluster by 的区别?

Order by会对所给的全部数据进行全局排序,只启动一个reduce来处理。

Sort by是局部排序,它可以根据数据量的大小启动一到多个reducer来工作,并且在每个reduce中单独排序。

Distribute by 类似于mr中的partition,采用hash算法,在map端将查询结果中hash值相同的结果分发到对应的reduce中,结合sort by使用

Cluster by 可以看作是distribute by 和sort by的结合,当两者后面所跟的字段列名相同时,效果就等同于使用cluster by,但是cluster by最终的结果只能是降序,无法指定升序和降序。

6. 如何将数据以动态分区的方式插入分区表中?
  • 1.首先创建对应分区表和一张普通表

  • 2.然后将数据加载到普通表

load data local inpath '/opt/bigdata/order_partition' into table tt_order;
  • 3.最后利用普通表来将数据加载到动态分区表中
先设置使用动态分区的参数和使用非严格模式
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict; 

然后通过普通表导入分区表
insert into table order_partition partition(year,month) select order_number,order_price,substring(order_time,0,4) as year,substring(order_time,6,12) as month from tt_order;
注意导入的字段顺序,分区键一定要放在最后,否则会报错。
7. 数据倾斜现象和解决办法?(重要)
  • 1.什么是数据倾斜?

    大量相同特征的key出现在同一个reduce任务中,或者某个key对应的数据量远超过其它key的数据量,这种导致数据分布不均匀的现象就叫做数据倾斜。

  • 2.数据倾斜的现象

    在执行任务的时候,任务进度长时间卡在99%左右,查看任务监控页面或者详细日志信息,发现只有少量,一个或者几个reduce子任务没有跑完,主要因为这几个reduce任务处理的数据量和其它reduce任务差异过大。这种单一reduce任务的记录数与平均记录数差异过大,,就会极大拖长计算时间。

    现实工作中可能会遇到这样的情况比较多:比如大表join小表,其中小表有特别的key值比较集中,这样分发到某一个reduce上的数据就会高于平均值;或者是大表join大表中,作为连接判断的字段0值或者空值较多的,这些0值和空值后续都会由一个reduce处理,导致这个reduce处理量过多;再有的情况就是group by、count( distinct )某个字段值数据多而导致reduce处理耗时的情况。

  • 3.数据倾斜的原因

    • key分布不均匀,比如空值,0值
    • 业务数据本身的特性。
    • 建表时考虑不周,导致后期join操作时数据倾斜
    • 某些sql语句本身就有数据倾斜。比如用count(distinct),它会单独用一个reduce来计算统计,如果数据量很大,就会导致整个job很难完成。这种情况可以先用group by分出需要统计的字段,再进行sum或者count
  • 4.数据倾斜的解决方案,有三个层面可以思考处理:

    • 第一,SQL语句调优

      • 查询语句加上具体需要的列和分区键,有些复杂表的字段会存储json格式的文本,这些字段不一定是需要查询的就可以过滤掉,减轻reduce计算负担

      • 大表join小表时用map jion,让小表先进内存,然后大表与小表在map端完成join操作,避免reduce端处理。

      • 大表join大表中,可以把空值的key变成一个字符串然后加上rand()随机数,后续mr的分区操作会把倾斜的数据重新分发到不同的reduce上,从而避免数据倾斜。或者在join 的on条件中先让key为空的值 不参与关联,等key不为空的数据相互合并连接后再union all加回key为空的数据。

        select * from a left outer join b 
        on case where id is null then concat('任意字符串',rand()) else id end = b.id;
        
        select * from log a join users b on a.id is not null and a.id = b.id
        union all
        selct * from log a where a.id is null;
        
      • 查询语句中count(distinct) 改成group by + sum(),比如

        select count(distinct id) from test; ==> select sum(id) from (select id from test group by id); 这种可能会多开一个reduce来完成group by的操作,但会明显提高查询速度。

      • 针对不同数据类型产生的数据倾斜,存在这样的情况,A表中的id字段的数据类型是int,但join的B表中id字段存在脏数据,有一些是int类型但也有string类型的,那么再join操作时,默认的hash操作就会对int类型的key进行分配,而对于string类型的key会被统一分配到一个reduce中,这种情况就需要先进行类型转换,如 a join b on a.id = cast(b.id as int);

      • 还有一些时候可以把数据倾斜的数据单独拿出来处理,然后再union all回去。

    • 第二,通过设置hive参数配置解决,这种主要是优化计算速度,避免数据倾斜发生

      • 开启map端聚合

        并不是所有的聚合操作都需要在reducec端完成,很多聚合操作都可以现在map端先进行部分聚合,最后在reduce端得出最终结果(类似于mr过程中的combiner,预先合并压缩数据,再提供给reduce统计计算)

        再hive开启map端聚合后,一旦发现数据倾斜,系统就能自动负载均衡,把相同特征的key分发到不同的reduce中,主要通过hive.groupby.skewindata参数完成。

        开启map端聚合的设置
        是否在map段進行聚合,默认是true
        set hive.map.aggr = true
        在map端进行聚合操作的条目数目
        set hive.groupby.mapaggr.checkinterval = 100000;
        有数据倾斜的时候进行负载均衡,比如把相同特征的key分发到不同的reduce中(默认是falseset hive.groupby.skewindata = true;
        
      • 设置并行执行

        和oracle一样也可以利用并行执行提高查询速度,不同的是hive是靠参数来空值的

        开启并行执行
        set hive.exec.parallel = true;
        设置同一个sql允许的最大并行度,默认是8
        set hive.exec.parallel.thread.number = 16;
        
      • 设置压缩

        压缩可以在map端要进行shuffle时压缩和在完成reduce输出时压缩

        • Hive表中间数据压缩
        设置为true为激活中间数据压缩功能,默认是false,没有开启
        set hive.exec.compress.intermediate = true;
        设置中间数据的压缩算法
        set mapred map.output.compression = codec = org.apache.hadoop.io.compress.SnappyCodec;
        
        • Hive表最终输出结果压缩
        set hive.exec.compress.output = true;
        set mapred map.output.compression = codec = org.apache.hadoop.io.compress.SnappyCodec;
        
      • 推测执行

        说简单点就是Hadoop用了一个备胎来同时执行,跟原来的任务相比较,谁先执行完成就用谁的计算结果作为最终的计算结果。具体定义如下:

        ​ Hadoop采用了推测执行机制,它根据一定的法则推测出”拖后腿“的任务,并为这样的任务启动一个备份任务,让备份任务和原始任务同时处理一份数据,并最后选择优先执行完成的任务计算结果作为最终结果。

        #开启推测执行机制
        set hive.mapred.reduce.tasks.speculative.exection = true;
        
      • JVM重用

        JVM重用可以使得JVM实例在同一个job中重新使用多次,减少进程的启动和销毁时间

        #设置jvm重用个数
        set mapred.job.reuse.jvm.num.tasks = 5;
        
      • 合理设置map数和reduce数

        • 在map执行之前将小文件合并可以减少map数
        系统默认的格式,可以不用设置。
        set hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; 
        
        • 对复杂文件可以增加map数
        增加map方法有一个公式:
        compute(SliteSize(Math.max(minSize,Math.min(maxSize,blocksizs))))公式
        - 调整maxSize最大值,让maxSize小于blocksize就可以增加map数
        - minSize默认等于1,maxSize默认等于blockSize大小。
        
        比如这样设置就可以达到增加map数的效果
        设置每个map处理的文件maxSize大小为10M,这样小于一个block128M的话,系统就会分配更多10M的map来处理复杂任务。
        set mapreduce.input.fileinputformat.split.maxsize = 10485760;
        
        • 合理设置Reduce数,比如设置每个job中reduce的个数为3个
        set mapreduce.job.reduces = 3;
        
    • 第三,修改MR程序去避免数据倾斜

      • 可以在MR程序的reduce方法中追踪每个键的最大值,并且设置阈值,当超过该阈值时就可以认为发生了数据倾斜,然后输出到日志文件进行分析。

      • 第二种是在编写MR程序时,从业务层面去考虑自定义的分区键是否合理。就跟ADS库建表时可以默认指定哪个字段作为分区键。

      • MR程序中改用TotalOrderPartitioner替换HashPartitioner,它可以通过对原始数据进行抽样得到的结果集来预设分区边界值,也就是能找出导致数据倾斜的key值,再分散处理。

      • MR程序中使用Combiner。

修改记录

时间内容
2020年04月10日第一次发布
2020年9月13日结合新课程,重新整理知识点框架
2020年9月14日上传知识点结构图
  • 学习参考:

《开课吧-大数据开发高级工程师一期》课程
《尚硅谷大数据项目数据仓库,电商数仓V1.2新版》课程

本文为学习课程做的笔记,如有侵权,请联系作者删除。
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值