Hive学习总结
1、离线数据仓库
是由FACEBOOK开源,基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类SQL查询功能极大提高了数据开发的效率
本质是一个Hadoop客户端
(1) Hive中每张表的数据存储在HDFS
(2) Hive分析数据底层的实现是MapReduce,也可配置为Spark或者Tez
(3) 执行程序运行在Yarn上
2、结构
CLI:命令行客户端
JDBC:远程客户端
3、编译流程
(1) 解析器:将SQL字符串转换成抽象语法树AST
(2) 语义分析:将AST进一步划分为QeuryBlock(抽象语法树-》查询单元)
(3) 逻辑计划生成器:将语法树生成逻辑计划
(4) 逻辑优化器:对逻辑计划优化,例如过滤操作前移
(5) 物理计划生成器:根据优化后的逻辑计划生成物理计划
(6) 物理优化器:对物理计划优化,例如把MR过程转换成多个M过程
(7) 执行器:执行计划,返回查询结果给客户端
3、安装
hive内部默认有数据库derby
两个模式
(1) 独立模式
(2) 嵌入模式,应用会独占服务
4、安装遇到的问题
(1) Class path contains multiple SLF4J bindings
这里是因为hive 和 Hadoop里的类名相同起冲突了,需要删除hive-lib下的内容,具体可以看报错路径
(2) java.lang.NoSuchMethodError: com.google.common.base.Preconditions.checkArgument
这里是hive 和 hadoop中guava.jar 版本不同导致,需要用高版本的guava.jar去覆盖低版本的
解决方法:
https://blog.csdn.net/weixin_46584887/article/details/121460816
5、derby数据库说明
derby数据库初始化构建的数据库在同一时刻只能给一个用户使用,作为最小化部署模式不适合生产环境
6、两大服务
(1)Hiveserver2
提供了jdbc/odbc接口,让用户能够远程访问hive数据
远程访问hive数据时实际上是由Hiveserver2代理进行访问,访问的用户身份可能是Hiveserver2的启动用户,也有可能是登录用户。
存在参数hive.server2.enable.doAs进行觉得,若启用则Hiveserver2会模拟成客户端的登录用户去访问hadoop集群的数据(用户模拟)
生产环境需要启动用户模拟功能
7、metastore服务
Hive的metastore服务的作用是为Hive CLI或者Hiveserver2提供元数据访问接口。
(1) metastore运行模式
【1】嵌入式模式
将metastore作为依赖嵌入到上述的服务和客户端中,嵌入模式需要每个客户端都直接连接数据库,客户端较多时数据库压力会比较大。且每个客户端若都能拿到数据库的读写权限,则无法保证安全
【2】独立服务模式
单独启动一个metastore服务单独访问数据库,不存储元数据,只负责提供访问数据的接口,生产环境常用
(2) 常用命令
-H help
-e + 命令 非交互式使用模式,用重复执行任务
-f + 文件 命令过多时放入文件再进行访问执行
(3) 参数配置方式
【1】文件方式
用户自定义:hive-site.xml
默认配置: hive-default.xml (别用)
用户自定义会覆盖默认配置,另外hive还会读入hadoop的配置所以也会让hive的配置覆盖hadoop的
【2】命令行参数方式
在hive启动时添加-hiveconf param=value来设定参数,此方式只对本次hive启动有效
【3】参数声明方式
使用set关键字设定参数,也仅仅对hive启动有效
上述三种设定方式的优先级依次递增。即配置文件 < 命令行参数 < 参数声明
8、DDL数据定义
(1) 怎么去定义数据、表
使用extend后可以查看更多信息
(2) 如何对数据进行操作处理
【1】修改
【2】删除
(1) 建表
【1】普通建表
类型转换
Hive的基本数据类型可以做类型转换,转换的方式包括隐式(自动)转换以及显示(手动)转换。方式一:隐式转换
具体规则如下:
a、任何整数类型都可以隐式地转换为一个范围更广的类型,如tinyint可以转换成int,int可以转换成bigint。b、 所有整数类型、float和string类型都可以隐式地转换成double。
c、tinyint、smallint、int都可以转换为float。
d、boolean类型不可以转换为任何其它的类型。
方式二:显示转换
可以借助cast函数完成显示的类型转换
a、语法
cast(expr as <type>)
b、关键字
ROW FORMAT : 表示处理一行(流数据)所用的格式
语法一:
DELIMITED关键字表示对文件中的每个字段按照特定分割符进行分割,其会使用默认的SERDE对每行数据进行序列化和反序列化。
语法二:
SERDE关键字可用于指定其他内置的SERDE或者用户自定义的SERDE。例如JSON SERDE,可用于处理JSON字符串。
STORED AS : 表示文件整体处理的格式
指定文件格式,常用的文件格式有,textfile(默认值),sequence file,orc file、parquet file等等。
PARTITIONGED BY:指定相同分区字段的值的数据放在同一分区,例如日期
CLUSTERED BY ... SORTED BY...INTO ... BUCKETS:创建分桶表
将一张表的数据分散存储在不同文件中
【2】CTAS建表-create table as select
使用select的查询语句返回结果建表。不能用于创建外部表
【3】CTL语法
允许用户复制一张已经存在的表结构,内部不包含数据
(2) 查看表
【1】show tables [IN database_name] LIKE ['identifier_with_wildcards'];
【2】DESCRIBE [EXTENDED | FORMATTED] [db_name.]table_name
查看一个表的具体信息
extend 列出了更加详细的信息
formated 会将这些信息格式化
(3) 修改表
【1】重命名
ALTER TABLE table_name RENAME TO new_table_name
【2】修改列的信息
这三个语法只是修改表的元数据信息(表的各列名字等),不会修改列中数据增加列:(末尾)
ALTER TABLE table_name ADD COLUMNS (col_name data_type [COMMENT col_comment], ...)
更新列:
ALTER TABLE table_name CHANGE [COLUMN] col_old_name col_new_name column_type [COMMENT col_comment] [FIRST|AFTER column_name]
替换列:
允许用户用新的列解体替换表中原有的列
ALTER TABLE table_name REPLACE COLUMNS (col_name data_type [COMMENT col_comment], ...)
(4) 删除表
DROP TABLE [IF EXISTS] table_name;
只是会删除表的元数据,hdfs文件不会被清理
(5) 清空表
TRUNCATE [TABLE] table_name
truncate只能清空管理表,不能删除外部表中数据。
10、DML数据操作
(1) Load
将文件导入到hive的表中
LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)];
【1】local:表示从本地加载数据到Hive表;否则从HDFS加载数据到Hive表,加载结束后文件会被移走
【2】INPARTH 原始数据路径
【2】overwrite:表示覆盖表中已有数据,否则表示追加。
【4】INTO TABLE 导入的表,into代表的是追加数据
【5】partition:表示上传到指定分区,若目标是分区表,需指定分区。
(2) insert
【1】查询结果插入表中
INSERT (INTO | OVERWRITE) TABLE tablename [PARTITION
into 是追加overwrite 是覆盖partition 告知分区
【2】将给的那个VALUES插入表中
INSERT (INTO | OVERWRITE) TABLE tablename [PARTITION (partcol1[=val1], partcol2[=val2] ...)] VALUES values_row [, values_row ...]
【3】将查询结果写入目标路径
INSERT OVERWRITE [LOCAL] DIRECTORY directory [ROW FORMAT row_format] [STORED AS file_format] select_statement;
将指令查询结果按照指定格式(行、文件)进行文件写入
路径最好写一个不存在的路径,因为会覆盖掉原路径下的内容
(3) export & inport
Export导出语句可将表的数据和元数据信息一并到处的HDFS路径,Import可将Export导出的内容导入Hive,表的数据和元数据信息都会恢复。Export和Import可用于两个Hive实例之间的数据迁移。
--导出
EXPORT TABLE tablename TO 'export_target_path'
--导入
IMPORT [EXTERNAL] TABLE new_or_original_tablename FROM 'source_path' [LOCATION 'import_target_path']
11、查询
having 是对分组后的结果进行过滤、
limit 后面跟一个参数代表返回前n行,若两个参数则代表从第n行开始往后抓取m行顺序不能颠倒
12、关系运算函数
13、聚合函数
14、Join函数
对两个或者多个表进行横向的拼接,Hive支持通常的sql join语句,支持等值连接并支持非等值连接。
(1) 语法
select ... from ... join ... on a.XX = b.XX
(2) 等值&不等值连接
等值连接:A表字段=B表字段 不等值连接:A表字段!=B表字段
(3) 内连接
inner join : 显示能够匹配上的所有数据,匹配不上的不显示
(4) 左外连接
left join : 以左边的表为主表进行拼接,返回左表的所有行,遇到空的列拼接则显示null
(5) 右外连接
right join: 和 上述相反
(6) 全外连接
full join :返回A表和B表的所有行和列,空余部分使用null补全
(7) 多表连接
执行逻辑:先用A join B,再将得到的表C和D进行join 关联
(8) 笛卡尔积方法一:
select * from A join B on true
方法二:
select * from A,B
(9) 写代码时
【1】分析关联字段
【2】分析包含关系
【3】明确想要的数据是什么
15、Union函数
两张表按列进行拼接,上下两张表的字段个数以及字段类型需要对应注意事项
(1) union 后面跟着的一定是select查询语句
(2) union相当于将多个select查询语句联合成了一个
(3) 会取第一个表的字段名作为最终表的字段名(属性)
(4) union all不会对完全相同的两段数据进行去重
16、order by
不要单独使用,要在后面加limit by ,这样在map阶段会进行优化,只需要返回limit by数量的数据即可
17、distribute by
指定mapreduce分区字段,主要和transform一起使用,但是用的非常少
18、sort by
指定从map 到 reduce 的排序字段
19、cluster by
若分区字段和排序字段为同一个字段,则可以简写成cluster by = distribute + sort
20、order by 和 sort by 的区别
order by 声明的是一个全局的排序字段,记得加limit
sort by 指定的是从map 到 reduce 的排序字段,只能保证每个reduce字段的排序有序,而不能保证全局有序
21、查询相关问题
在查询的过程中有时候会希望保留筛选的元素到虚拟表中,但不能直接添加,会有语法错误,course_id 在语法审核时不清楚该变量有多少参数,因此可能是多参数和一个参数(sum(score))组成一行,这是不允许的:
【1】使用group by进行筛选,筛选后能确定只有一个参数
【2】使用"02" 常量值+别名
22、多表全外连问题
当涉及到多个表进行全外连的时候,由于关键字可能会出现null,因此需要使用nvl(a,b)函数。该函数会进行一个if...not 判定,如果a为空那么可以使用b中的关键字函数
如果涉及到两个以上的表进行全外连需要选定关键字时可以使用coalesce(a,b,c) 23、函数
(1) 简介
(2) 单行函数
输入一行,输出一行
【1】算术运算函数
【2】数值函数
【3】字符串函数
substring(str,loc,[len])
用于截取字符串,参数二决定截取位置,参数三可选,代表截取长度,默认剩余全部
replace(str1,str2,str3) str1:原来的字符串
str2:需要替换的字符串,全局所有的对应片段str3:替换成的字符串
regexp_replace(str1,str2,str3)
正则替换
正则表达式遇到斜杠记得需要加两个\ regexp
和like函数很像,但是需要使用正则
split(str, 正则表达式)
按照指定正则表达式对字符串进行切割
concat(str1,str2,str3...)
字符串拼接,参数不限量
concat_ws
若是单一的分隔符,使用该函数更方便
get_json_object(string json_string,string path)
解析json字符串,json_string是需要解析的json字符串,path参数是返回path的指定内容,如果输入的json字符串无效,那么就返回null
【4】日期函数
unix_timestamp(string time,string format) //返回当前或者指定时间的时间戳(0时区自1910年以来所经历的时间),和机器所在时区无关 10位长的是秒,13位长的是毫秒 from_unixtime(string timestamp,string format) //转换unix时间戳到当前时区的格式,会转成0时区时间 from_utc_timestamp(string timestamp,string time_zone) //将时间戳转变成指定时区的函数,小数以秒为单位处理,整数以毫秒为单位处理若果使用整数处理,记得使用bigint类型转换后再×1000 current_day //返回当前日期 current_timestamp //返回当前时间戳 datediff(string time1,string time2) //计算两个日期的相差天数(第一个日期-第二个日期) date_add(string time,int days) //返回开始日期增加days天后的日期 date_sub //同上但是减去 months_between() //计算两个日期之间的月份间隔
【5】流程控制函数
case... when 判断条件 then 执行语句 : when...then... : else end
判断是否条件成立,是逐个条件判断。
如果case判断条件的参数都为同一个,只是数值发生了改变,那么可以把参数放在case后面,when后面只需要添加变化的数值
if :三元函数,判断成功执行a,否则b
sum(if()) :
根据条件求和,先对条件进行判断,然后返回统计结果,if的否认结果为0
【6】集合函数
array(val1,val2...) array_contains(array, val) //判断array数组中是否包含元素val sort_array(array) //将array中的元素排序 size(array) //判断集合中的元素个数 map(key1,val1,key2,val2...) map_keys //返回map中的keys,返回数组 map_values //返回map中的values,返回数组,不会去重 struct(val1,val2,...) //只声明struct中的各个属性 named_struct //声明struct的属性和值
【7】高级聚合函数
collect_list
收集并形成list集合,结果不去重
collect_set
收集并形成set集合,结果去重
(3) 炸裂函数(UDTF)
接收一行数据,输出一行或多行数据用户定义的制表函数
explode(array<T>a)
explode(Map<K,V> m)
posexplode(array<T> a)
inline(array<struct<f1:T1,...fn:Tn>> a)
Lateral View
通常与UDTF配合使用,它可以将UDTF应用到源表的每行数据,将每行数据转换为一行或者多行,并将源表中每行的输出结果与该行连接起来,形成一个虚拟表
select from 给出的表不是左边的表,而是构造后的右边的表
(4) 窗口函数
为每行数据划分一个窗口,然后对窗口范围内的数据进行计算,最后将计算结果返回给该行数据窗口函数主要包括窗口和函数两个部分,窗口用于定义计算范围,函数用于定义计算逻辑
【1】语法
绝大多数的聚合函数都可以配合窗口使用,例如max(),min(),sum(),count(),avg()等
窗口范围分为两种类型,一种是基于行的,一种是基于值的
基于行:
unbounded preceding 最前面一个元素
[num] preceding 前面第 num 个元素
[num] following 后面第num个元素
基于行是指在计算的时候按行划定窗口,而不是说依照原表进行按行的窗口划定,因此需要使用order by来规定次序
基于值:
order by 的作用是基于哪个字段的值进行划分窗口,在使用涉及到num的参数时需要使用order by的整数类型
定义窗口范围时,可以指定分区字段,每个分区单独划分窗口
部分内容在over()中都可以省略不写
常用窗口函数
聚合函数
跨行取值函数
lead & lag
获取当前行的上/下边某行、某个字段的值,只能用于基于行,且不支持自定义窗口,lead代表后几行,lag代表前几行
first_value & last_value
获取窗口内某一列第一个或者最后一个值
第二个布尔参数代表是否要跳过NULL值,如果下一行是NULL,那么跳到下下一行
rank、dense_rank、row_number
排名函数,不支持自定义窗口
rank 是稀疏排名,并列跳名次 ,1 1 3
dense_rank 是密集排名,并列不跳名次,1 1 2
row_number 是按行号排名,就算排序用的值相同也会按行分出先后次序
(5) 常用去重方法
【1】distinct 函数
【2】group by 函数
【3】窗口函数去重
over ( partition by ...) rn
where rn = 1 //需要筛选的关键字
(6) 自定义函数
【1】临时函数
临时函数只跟会话有关系,跟库没有关系。只要创建临时函数的会话不断,在当前会话下,任意一个库都可以使用,其他会话全都不能使用。
【2】永久函数
因为add jar本身也是临时生效,所以在创建永久函数的时候,需要制定路径永久函数跟会话没有关系,创建函数的会话断了以后,其他会话也可以使用。
永久函数创建的时候,在函数名之前需要自己加上库名,如果不指定库名的话,会默认把当前库的库名给加上。永久函数使用的时候,需要在指定的库里面操作,或者在其他库里面使用的话加上,库名.函数名。
附注:永久函数名前会附注上所在数据库名,查询时请加上‘*’
24、分区表
Hive中的分区就是把一张大表的数据按照业务需要分散的存储到多个目录,每个目录就称为该表的一个分区。在查询时通过where子句中的表达式选择查询所需要的分区,这样的查询效率会提高很多
(1) 语法
(2) 读写数据
【1】写数据
load
insert
【2】基本操作
show partitions ****
查看所有分区信息
alter table *** add partition(*** = '***')
增加分区
【3】删除分区
alter table *** drop partition(*** = '***') //删除单个分区 alter table *** drop partition(*** = '***'), partition(*** = '***') //删除多个分区
附注:外部表只删除元数据,而不删除HDFS上的路径,内部表才会删除
【4】修复分区
Hive将分区表的所有分区信息都保存在了元数据中,只有元数据与HDFS上的分区路径一致时,分区表才能正常读写数据。若用户手动创建/删除分区路径,Hive都是感知不到的,这样就会导致Hive的元数据和HDFS的分区路径不一致。
再比如,若分区表为外部表,用户执行drop partition命令后,分区元数据会被删除,而HDFS的分区路径不会被删除,同样会导致Hive的元数据和HDFS的分区路径不一致。
若出现元数据和HDFS路径不一致的情况,可通过如下几种手段进行修复。
(a) add partition
若手动创建HDFS的分区路径,Hive无法识别,可通过add partition命令增加分区元数据信息,从而使元数据和分区路径保持一致。
(b) rop partition
若手动删除HDFS的分区路径,Hive无法识别,可通过drop partition命令删除分区元数据信息,从而使元数据和分区路径保持一致。
(c) msck
若分区元数据和HDFS的分区路径不一致,还可使用msck命令进行修复说明:
msck repair table table_name add partitions:
该命令会增加HDFS路径存在但元数据缺失的分区信息。
msck repair table table_name drop partitions:
该命令会删除HDFS路径已经删除但元数据仍然存在的分区信息。
msck repair table table_name sync partitions:
该命令会同步HDFS路径和元数据分区信息,相当于同时执行上述的两个命令。
msck repair table table_name:
等价于msck repair table table_name add partitions命令。
以上三个命令实际上都是依照HDFS的路径去修复mysql中的元数据,其目的都是为了修改显示出来的元数据
(3) 二级分区
【1】语法
只需要在一级分区的基础上添加字段就是二级分区了
【2】载入与查询
(4) 动态分区
动态分区是指向分区表insert数据时,被写往的分区不由用户指定,而是由每行数据的最后一个字段的值来动态的决定(就是最后一行的在实际数据中不存在的partition字段所决定),在编写代码的时候需要再最后添加所进行分区的字段名。使用动态分区,可只用一个insert语句将数据写入多个分区。
【1】参数
【2】实操
25、分桶表
分区提供一个隔离数据和优化查询的便利方式。不过,并非所有的数据集都可形成合理的分区。对于一张表或者分区,Hive 可以进一步组织成桶,也就是更为细粒度的数据范围划分,分区针对的是数据的存储路径,分桶针对的是数据文件。
分桶表的基本原理是,首先为每行数据计算一个指定字段的数据的hash值,然后模以一个指定的分桶数,最后将取模运算结果相同的行,写入同一个文件中,这个文件就称为一个分桶(bucket)。
在一个表中分桶与分区不矛盾,它会对每个分区进行分桶。但是对于一个字段不能这么做
(1) 语法
在使用load移动文件到HDFS的时候会按照分桶数量划分出对应数量的文件,3.* hive进行了增强,若load的目标表是一个分桶表则会按照分桶数量划分文件
(2) 分桶排序表
26、文件格式和压缩
(1) 前置复习,压缩格式
(2) 文件格式
为Hive表中的数据选择一个合适的文件格式,对提高查询性能的提高是十分有益的。Hive表数据的存储格式,可以选择text file、orc、parquet、sequence file等。
【1】存储结构行存储的特点:
查询满足条件的一整行数据的时候,列存储则需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。
列存储的特点:
因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量;每个字段的数据类型一定是相同的,列式存储可以针对性的设计更好的设计压缩算法。
前文提到的text file和sequence file都是基于行存储的,orc和parquet是基于列式存储的。
总结:
如果是按行进行查询则使用行存储,否则使用列存储
【2】text file(行存储)
默认文件格式文本文件中的一行内容,就对应Hive表中的一行记录。
【3】ORC(列存储)
ORC(Optimized Row Columnar)file format是Hive 0.11版里引入的一种列式存储的文件格式。ORC文件能够提高Hive读写数据和处理数据的性能。
基本格式
Stripe Footer:
对表中每列内容进行编码(序号)信息,用编码进行存储使得数据量可以减小
在ORC中,由于存在大量的索引、长度、起始位置信息,使得在数据读取时可以从Postscript开始一步步反推各个模块的长度和起始地址,进而推出各个stripe的其实地址。最后再读取index data和stripe footer可以得到数据,进而加快读取速度
建表语句及参数
ORC文件是分块压缩的,每个stripe的大小通常为128M,最后一行参数可以不加
【4】Parquet
Parquet文件是Hadoop生态中的一个通用的文件格式,它也是一个列式存储的文件格式。
基本格式
首尾中间由若干个Row Group和一个Footer(File Meta Data)组成。
每个Row Group包含多个Column Chunk,每个Column Chunk包含多个Page。以下是Row Group、Column Chunk和Page三个概念的说明:
行组(Row Group):一个行组对应逻辑表中的若干行。
列块(Column Chunk):一个行组中的一列保存在一个列块中。
页(Page):一个列块的数据会划分为若干个页。
Footer(File Meta Data)中存储了每个行组(Row Group)中的每个列快(Column Chunk)的元数据信息,元数据信息包含了该列的数据类型、该列的编码方式、该类的Data Page位置等信息。
建表语句
通常bolck大小为128M,按照page大小压缩,默认为1M
(3) 压缩
在Hive表中和计算过程中,保持数据的压缩,对磁盘空间的有效利用和提高查询性能都是十分有益的,不同文件类型的表,声明数据压缩方式不同
【1】textfile
若一张表的文件类型为TextFile,若需要对该表中的数据进行压缩,多数情况下,无需在建表语句做出声明。直接将压缩后的文件导入到该表即可,Hive在查询表中数据时,可自动识别其压缩格式,进行解压。
需要注意的是,在执行往表中导入数据的SQL语句时,用户需设置以下参数,来保证写入表中的数据是被压缩的。
--SQL语句的最终输出结果是否压缩
set hive.exec.compress.output=true;
// insert 最终往表里输出的数据是压缩的
--输出结果的压缩格式(以下示例为snappy)
set mapreduce.output.fileoutputformat.compress.codec =org.apache.hadoop.io.compress.SnappyCodec;
如果使用load导入数据,load的文件必须是压缩好的文件。insert插入数据则需要设置参数
【ORC】
【parquet】
(4) 计算过程中的压缩(都要落盘)
建议大家开启且采用snappy格式,速度快一些
27、调优(面试爱问)
(1) 计算资源配置(基于MR)
【1】yarn配置
yarn.nodemanager.resource.memory-mb
该参数的含义是,一个NodeManager节点分配给Container使用的内存。该参数的配置,取决于NodeManager所在节点的总内存容量和该节点运行的其他服务的数量。
考虑上述因素,此处可将该参数设置为64G(默认8G),如下:
yarn.nodemanager.resource.cpu-vcores
该参数的含义是,一个NodeManager节点分配给Container使用的CPU核数。该参数的配置,同样取决于NodeManager所在节点的总CPU核数和该节点运行的其他服务。
考虑上述因素,此处可将该参数设置为16(默认8个)。
yarn.scheduler.maximum-allocation-mb
该参数的含义是,单个Container能够使用的最大内存。推荐配置如下:
yarn.scheduler.minimum-allocation-mb
该参数的含义是,单个Container能够使用的最小内存,推荐配置如下:
【2】MR资源配置
MapReduce资源配置主要包括Map Task的内存和CPU核数,以及Reduce Task的内存和CPU核数。核心配置参数如下:
mapreduce.map.memory.mb
该参数的含义是,单个MapTask申请的container容器内存大小,其默认值为1024。该值不能超出yarn.scheduler.maximum-allocation-mb和
yarn.scheduler.minimum-allocation-mb规定的范围。
该参数需要根据不同的计算任务单独进行配置,在hive中,可直接使用如下方式为每个SQL语句单独进行配置:
mapreduce.map.cpu.vcores
该参数的含义是,单个Map Task申请的container容器cpu核数,其默认值为1。该值一般无需调整。
mapreduce.reduce.memory.mb
该参数的含义是,单个ReduceTask申请的container容器内存大小,其默认值为1024。该值同样不能超出yarn.scheduler.maximum- allocation-mb和yarn.scheduler.minimum-allocation-mb规定的范围。
该参数需要根据不同的计算任务单独进行配置,在hive中,可直接使用如下方式为每个SQL语句单独进行配置:
mapreduce.reduce.cpu.vcores
该参数的含义是,单个Reduce Task申请的container容器cpu核数,其默认值为1。该值一般无需调整。
【3】explain 概述
Explain呈现的执行计划,由一系列Stage组成,这一系列Stage具有依赖关系,每个Stage对应一个MapReduce Job,或者一个文件系统操作等。
若某个Stage对应的一个MapReduce Job,其Map端和Reduce端的计算逻辑分别由Map Operator Tree和Reduce Operator Tree进行描述, Operator Tree由一系列的Operator组成,一个Operator代表在Map或Reduce阶段的一个单一的逻辑操作,例如TableScan Operator,Select Operator,Join Operator等。
执行计划如图所示:
常见的operator有:
TableScan:表扫描操作,通常map端第一个操作肯定是表扫描操作Select Operator:选取操作
Group By Operator:分组聚合操作
Reduce Output Operator:输出到 reduce 操作
Filter Operator:过滤操作Join Operator:join 操作
File Output Operator:文件输出操作
Fetch Operator 客户端获取数据操作(客户端执行结束后需要拉取存储到HDFS的结果)
基本语法
注:FORMATTED、EXTENDED、DEPENDENCY关键字为可选项,各自作用如下。FORMATTED:将执行计划以JSON字符串的形式输出 EXTENDED:输出执行计划中的额外信息,通常是读写的文件名等信息(例如临时数据所在目录) DEPENDENCY:输出执行计划读取的表及分区(把需要读取的表和分区显示出来)
【4】分组聚合优化
Hive对分组聚合的优化主要围绕着减少Shuffle数据量进行,具体做法是map-side聚合。所谓map-side聚合,就是在map端维护一个hash
table,利用其完成部分的聚合,然后将部分聚合的结果,按照分组字段分区,发送至reduce端,完成最终的聚合。map-side聚合能有效减少shuffle的数据量,提高分组聚合运算的效率。
参数
不适合进行Map 聚合的场景:
只存在一种分组字段的表去执行分组指令,分了等于没分
相同关键字段数据条目过多的情况(伪不适),可通过调整红框内的参数强制进行map-side整合
【5】join的种类
common join
sql语句中的join操作和执行计划中的common join任务并非是一对一的关系,一个sql语句中相邻且关键字段相同的多个join可以合并成一个common join任务
map join
使用两个只有map阶段的join去完成任务,其适用场景为大表join小表,不适用于大表对大表
第一个join会先从分布式小表中读取数据,并将其制作为hash table,并上传到hadoop分布式缓存(HDFS);第二个job会先从分布式存储中读取小表数据,并缓存在map task的内存中,然后扫描大表数据,这样在map端便可以完成map操作
bucket map join
打破限制,适用于大表 map 大表,在小表做hash table的时候要分桶做(hash join)
SMP join
和主要不同在于桶内数据需要排序,和前文不同的地方还有SMP中两个分桶之间join的实现原理为SMP Merge Join算法
由于表中数据是有序的,因此每次取数据时不用取出所有的数据,只需要一直取到关键字段发生变更即可停止取出,然后将两张表中的数据执行join操作
该方法的优势在于不需要用哈希表,对内存要求更低(对分桶大小没有要求了)
【6】map Join 优化触发方式
Map Join有两种触发方式,一种是用户在SQL语句中增加hint提示,另外一种是Hive优化器根据参与join表的数据量大小,自动触发。
(a) Hint提示
用户可通过如下方式,指定通过map join算法,并且ta将作为map join中的小表。这种方式已经过时,不推荐使用。
(b) 自动触发
Hive在编译SQL语句阶段,起初所有的join操作均采用Common Join算法实现。
之后在物理优化阶段,Hive会根据每个Common Join任务所需表的大小判断该Common Join任务是否能够转换为Map Join任务,若满足要求,便将Common Join任务自动转换为Map Join任务。(用户设置一个阈值,决定内存能够缓存数据的大小)
但有些Common Join任务所需的表大小,在SQL的编译阶段是未知的(例如对子查询进行join操作),所以这种Common Join任务是否能转换成Map Join任务在编译阶是无法确定的。
针对这种情况,Hive会在编译阶段生成一个条件任务(Conditional Task),其下会包含一个计划列表,计划列表中包含转换后的Map Join任务以及原有的Common Join任务。最终具体采用哪个计划,是在运行时决定的。大致思路如下图所示:
执行流程
(a)noconditionaltas
初始化设置,判断T 和 F,默认T
(b) 寻找大表候选人:
如果是内联,则两张表都能做大表;
如果是左联,则左表作为大表,右表进入内存存储
如果是右联,则右表作为大表,左表进入内存存储
如果是全联,则无法找出大表
(c) 排除不可能实现的计划
若除了大表以外的所有小表之和大于了小表大小的阈值,则该计划被排除
(d) 子任务map join合并条件子任务也是map join
子任务和当前任务的所有小表大小已知他们的总和小于小表阈值
则可以合并
(e) 多个字段的多表map join
由于各个表之间join的字段并不一样,因此会分成多个join task,然后再按照上述流程执行,若两个task 都可以使用map join,则可以考虑合
并
(f) 涉及参数
map join 优化方案
(a) 方案一: 只启动map join自动转换,无条件转换为false noconditionaltas设置为false,直接走右边,不合并
(b) 方案二:启动map join 自动转换和无条件map join转换,设置阈值大于多表之和(最优) noconditionaltas这只true,并设置内存大小为多表之和便于合并多表的map reduce。该方法速度最快但内存消耗最多
(c) 方案三:启动map join 自动转换和无条件map join转换,设置阈值小于多表之和同上,时间消耗较多但是内存消耗较少
一般来说,配的内存大小和文件实际大小的配比是10:1,也就是说,划分给小表的内存为1G的话,实际能存储的文件大小最大为100M
【7】bucket map join 优化
不支持自动转换,必须通过配置hint提示实现,不支持原因是因为官方已经放弃MR引擎了
【8】SMP Map join
(a)触发方式
两种,包括自动转换和hint 提示
【9】总结
(a)map reduce占用的内存最好为总内存的1/2~2/3之间
(b)map reduce 的可使用内存大小和实际参与操作的文件大小比为10:1
(c)bucket map join 的一个桶占用空间最好不要超过500M
(2) 数据倾斜
【1】分组导致的数据倾斜优化
(a)map-side 聚合(小表对大表)
开启后,数据会先在map 端完成聚合工作,这样一来即使数据倾斜,经过map端的初步聚合后,发往reduce的数据也就不再倾斜了。最佳状态是可以完全屏蔽该问题,耗费内存。
相关参数
(b)skew-groupby优化
原理是启动两个MR任务,第一个MR按照随机数分区,将数据分散发送到reduce,完成部分聚合,第二个MR按照分组字段分区,完成最终聚合。
相关参数
【2】join 导致的数据倾斜
未经优化的join操作,默认是使用common join算法,也就是通过一个MapReduce Job完成计算。Map端负责读取join操作所需表的数据,并按照关联字段进行分区,通过Shuffle,将其发送到Reduce端,相同key的数据在Reduce端完成最终的Join操作。
如果关联字段的值分布不均,就可能导致大量相同的key进入同一Reduce,从而导致数据倾斜问题。有三种优化方法
(a) map join
使用map join,使得join操作只在map端完成,没有shuffle操作,没有reduce阶段,固不会产生数据倾斜
表中的数据会按照相同的数据量进行切片移交给不同的map进行处理,每个map上都有一个小表,只需要匹配输出即可
(b)skew join(适用于大表对大表)
原理为为倾斜的大key单独启动一个map join 任务进行计算,其余key进行正常的common join
参数
该方案对参与join的源表大小没要求,但是对两表中倾斜的key数据量有要求,要求其中一张表中的倾斜key数据量比较小(方便走map join )
(c)调整sql语句(适合均为大表)
一个打散,一个扩容
利用rand()*2 as int 将表的内容打散成两部分另一个表添加字段便于join操作,相当于扩容通过这种方式打散了倾斜的数据
(3) 并行度优化
对于一个分布式的计算任务而言,设置一个合适的并行度十分重要。Hive的计算任务由MapReduce完成,故并行度的调整需要分为Map端和
Reduce端。
【1】map端并行度
Map端的并行度,也就是Map的个数。是由输入文件的切片数决定的。一般情况下,Map端的并行度无需手动调整。以下特殊情况可考虑调整map端并行度:
(a)查询表中存在大量小文件
按照Hadoop默认的切片策略,一个小文件会单独启动一个map task负责计算。若查询的表中存在大量小文件,则会启动大量map task,造成计算资源的浪费。这种情况下,可以使用Hive提供 CombineHiveInputFormat,多个小文件合并为一个切片,从而控制map task个数。相关参数如下:
(b)map端有复杂的查询逻辑
若SQL语句中有正则替换、json解析等复杂耗时的查询逻辑时,map端的计算会相对慢一些。若想加快计算速度,在计算资源充足的情况下,可考虑增大map端的并行度,令map task多一些,每个map task计算的数据少一些。相关参数如下:
【2】reduce端并行度
Reduce端的并行度,也就是Reduce个数。相对来说,更需要关注。Reduce端的并行度,可由用户自己指定,也可由Hive自行根据该MR Job输入的文件大小进行估算。
Reduce端的并行度的相关参数如下:
之所以不准确很大程度上是因为map 端在输出的时候有可能进行聚合,一次和输入时的大小不一样,因此不准确所以如果遇到给的map端并行度过大浪费资源的情况可以人工设置参数
(4) 小文件合并
小文件合并优化,分为两个方面,分别是Map端输入的小文件合并,和Reduce端输出的小文件合并。
【1】map端(小文件合成为一个切片)
合并Map端输入的小文件,是指将多个小文件划分到一个切片中,进而由一个Map Task去处理。目的是防止为单个小文件启动一个Map Task,浪费计算资源。
参数:
【2】reduce端(小文件合并成大文件)
合并Reduce端输出的小文件,是指将多个小文件合并成大文件。目的是减少HDFS小文件数量。其原理是根据计算任务输出文件的平均大小进行判断,若符合条件,则单独启动一个额外的任务进行合并。
(5)其他优化
【1】CBO优化( cost based optimizer)
CBO是指Cost based Optimizer,即基于计算成本的优化。
在Hive中,计算成本模型考虑到了:数据的行数、CPU、本地IO、HDFS IO、网络IO等方面。Hive会计算同一SQL语句的不同执行计划的计算成本,并选出成本最低的执行计划。目前CBO在hive的MR引擎下主要用于join的优化,例如多表join的join顺序。
参数(默认开启):
根据上述案例可以看出,CBO优化对于执行计划中join顺序是有影响的,其之所以会将province_info的join顺序提前,是因为province info的数据量较小,将其提前,会有更大的概率使得中间结果的数据量变小,从而使整个计算任务的数据量减小,也就是使计算成本变小。
【2】谓词下推
谓词下推(predicate pushdown)是指,尽量将过滤操作前移,以减少后续计算步骤的数据量。
CBO优化也会完成一部分的谓词下推优化工作,因为在执行计划中,谓词越靠前,整个计划的计算成本就会越低。在写sql语句的时候尽量先过滤后join,这样子可以减少数据操作,提高效率
【3】矢量化查询
Hive的矢量化查询优化,依赖于CPU的矢量化计算,CPU的矢量化计算的基本原理如下图:
通过矢量化减少计算指令的调用
Hive的矢量化查询,可以极大的提高一些典型查询场景(例如scans, filters, aggregates, and joins)下的CPU使用效率。若执行计划中,出现“Execution mode: vectorized”字样,即表明使用了矢量化计算。
不是所有的类型都可以进行矢量化查询的,一些类型存在限制
【4】fetch 抓取
Fetch抓取是指,Hive中对某些情况的查询可以不必使用MapReduce计算。例如:select * from emp;在这种情况下,Hive可以简单地读取emp对应的存储目录下的文件,然后输出查询结果到控制台。
【5】本地模式优化
大多数的Hadoop Job是需要Hadoop提供的完整的可扩展性来处理大数据集的。不过,有时Hive的输入数据量是非常小的。在这种情况下, 为查询触发执行任务消耗的时间可能会比实际job的执行时间要多的多。对于大多数这种情况,Hive可以通过本地模式在单台机器上处理所有的任务。对于小数据集,执行时间可以明显被缩短。
【6】并行执行(没有依赖关系的stage执行)
Hive会将一个SQL语句转化成一个或者多个Stage,每个Stage对应一个MR Job。默认情况下,Hive同时只会执行一个Stage。但是某SQL语句可能会包含多个Stage,但这多个Stage可能并非完全互相依赖,也就是说有些Stage是可以并行执行的。此处提到的并行执行就是指这些Stage的并行执行。相关参数如下:
【7】严格模式
Hive可以通过设置某些参数防止危险操作: