一、Hive基础
1、Hive是什么?如何理解Hive?
Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能(HQL)。本质是将SQL转换为MapReduce的任务进行运算。
2、Hive和传统数据库比较
- 数据存储位置
Hive是建立在Hadoop之上的,因此所有的Hive数据都是存储在HDFS中的。而数据库则可以将数据保存在块设备或本地文件系统中。
- 数据更新
传统数据库是写模式,hive是读模式。传统数据库在写入数据的时候就去检查数据格式,而hive在读取数据的时候检查。因此,Hive中不建议对数据的改写。而数据库中的数据通常是需要经常进行修改。
- 执行
Hive基于Mapreduce,数据库基于执行引擎。
- 执行延迟
Hive在查询数据的时候,需要扫描整个表(或分区),因此延迟较高,只有在处理大数据是才有优势。数据库在处理小数据是执行延迟较低。
- 可扩展性
Hive高,数据库低
- 数据规模
Hive可以支持较大规模的数据计算;而数据库可以支持的数据规模较小。
3、Hive保存元数据的方式
Hive本身不存储数据,借助HDFS存储数据,由于元数据不断的修改、更新,所以不适合将元数据存在HDFS中,所以一般将元数据存储在第三方软件中,如derby、mysql中。
- 1、内嵌模式(Embedded)
内嵌模式使用的是内嵌的Derby数据库存储元数据,这个是默认的,配置简单,但由于Derby数据库每次只能访问一个数据文件,不支持多会话连接,所以不适合生产环境。
- 2、本地模式(Local)
将元数据保存在本地独立的数据库中(一般是mysql),支持多会话连接。
- 3、远程模式
将元数据保存在远程独立的mysql数据库中,避免每个客户端都去安装mysql数据库。需启动Metastore服务。
4、Hive数据类型
Hive数据类型可以分为基本数据类型和复杂数据类型。
- 基本数据类型
- 复杂数据类型
- 类型转换
1、任何整数类型都可以隐式地转换为一个范围更广的类型,如tinyint可以转换成int,int可以转换成bigint。
2、所有整数类型、float和string类型都可以隐式地转换成double。
3、tinyint、smallint、int都可以转换为float。
4、boolean类型不可以转换为任何其它的类型。
5、Hive存储格式
- textfile
Hive数据表的默认格式,数据不做压缩,磁盘开销大,数据解析开销大。存储方式:行存储。
- RCFile
RCFile是一种行列存储相结合的存储方式。首先,其将数据按行分块,保证同一个record在一个块上,避免读一个记录需要读取多个block。其次,块数据列式存储,有利于数据压缩和快速的列存取。
- ORCFile
存储方式:数据按行分块,每块按照列存储。 压缩快,可切分,快速列存取。效率比rcfile高,是rcfile的改良版本。支持各种复杂的数据类型,比如datetime,decimal,以及复杂的struct。
6、内部表和外部表的区别,以及各自的使用场景
- 内部表:
没有特别指定,则默认创建的表就是内部表。由Hive负责管理表中的数据,管理表不共享数据。删除管理表时,会删除管理表中的数据和元数据信息
- 外部表
当一份数据需要被共享时,可以创建一个外部表指向这份数据。
被external修饰的为外部表;外部表数据由HDFS管理;删除外部表仅仅会删除元数据,HDFS上的文件并不会被删除;
7、分区表和分桶表的区别
- 分区表:
Hive 数据表可以根据某些字段进行分区操作,细化数据管理,让部分查询更快,不同分区对应不同的目录。
好处:细化数据管理,缩小mapreduce程序 需要扫描的数据量。
- 分桶表:
分桶是相对分区进行更细粒度的划分。分桶将整个数据内容按照某列属性值的hash值进行区分,不同的桶对应不同的文件。
好处:提高join查询的效率,在一份数据会被经常用来做连接查询的时候建立分桶,分桶字段就是连接字段;提高采样的效率。
8、动态分区和静态分区
静态分区与动态分区的主要区别在于静态分区需要手动指定分区数值,而动态分区是通过数据来进行判断。
# 启用hive动态分区,只需要在hive会话中设置两个参数:
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
9、Hive的执行过程
1、用户提交查询任务给驱动器Driver
2、驱动程序将HQL发送给解析器和编译器,检查语法和生成查询计划
3、编译器Compiler则根据用户任务去MetaStore中获取需要的Hive的元数据
4、编译器Compiler得到元数据信息后,对任务进行编译,先将HiveQL转换为抽象语法树,然后将抽象语法树转换成查询块,将查询块转化为逻辑的查询计划,重写逻辑查询计划,将逻辑计划转化为物理的计划(MapReduce)
(其实就是经编译器编译后生成逻辑执行计划,优化器对逻辑执行计划优化后,执行器将逻辑计划转化成可以运行的物理计划,即MapReduce,简而言之就是将HQL转化成MapReduce)
5、然后将最终生成的计划提交给Driver,此时查询解析编译完成。
6、Driver将计划转交给执行器ExecutionEngine去执行
7、在内部,执行作业的过程是一个MapReduce工作。执行引擎发送作业给JobTracker,在名称节点并把它分配作业到TaskTracker,这是在数据节点。在这里,查询执行MapReduce工作,与此同时,在执行时,执行引擎可以通过Metastore执行元数据操作
8、执行引擎接收来自数据节点的结果并将这些结果发送给驱动程序。
9、驱动程序将结果发送给Hive接口。
10、Order By,Sort By,Distrbute By,Cluster By 的区别
- Order By:全局排序,只有一个Reducer
- Sort By:分区内有序
- Distrbute By:类似MR中Partition,进行分区,结合sort by使用
- Cluster By:当Distribute by和Sorts by字段相同时,可以使用Cluster by方式。Cluster by除了具有Distribute by的功能外还兼具Sort by的功能。但是排序只能是升序排序,不能指定排序规则为ASC或者DESC。
11、Hive窗口函数
- RANK() 排序相同时会重复,总数不会变,例如1224
- DENSE_RANK() 排序相同时会重复,总数会减少,例如 1223
- ROW_NUMBER() 会根据顺序去计算,例如 1234
12、Hive窗口函数
13、Union与Union all区别
- union会将联合的结果集去重,效率较union all差。
- union all不会对结果集去重,所以效率高。
14、Drop,delete 和 truncate 的区别
- drop 连表数据和表结构一起删除,操作完马上生效,不能回滚。
- delete仅删除表数据,在删除数据时是一行一行的删除,每删除一行数据,就在事务日志中为删除的那行数据做一项记录,操作完以后如果没有不想提交事务还可以回滚(roll back);
- truncate也是仅删除表数据,但是删除时是首先删除表和数据,然后创建一张新的表。
- 速度上:drop>truncate>delete
15、Hive数据倾斜原因及解决方案
1、数据倾斜原因
由于数据分布不均匀,造成数据大量的集中到一点,使某个reduce的数据输入量远远大于其他reduce数据的输入量。具体表现为任务进度长时间维持在99%(或100%),查看任务监控页面,发现只有少量(1个或几个)reduce子任务未完成。如下图所示:
报错信息一般如下:
AttemptID:attempt_1624419433039_1569885_r_000000 Timed outafter 600 secs Container killed by the ApplicationMaster. Container killed onrequest. Exit code is 143 Container exited with a non-zero exit code 143
2、查看数据倾斜
2.1、通过 yarnUI 查看每个 reduce 执行时间
如果有某个或某几个明显大于其他 reduce 执行时间。大概率发生了数据倾斜。
2.2、通过任务 counter 判断
counter 会记录整个 job 以及每个 task 的统计信息。通过输入记录数,普通的 task counter 如下,输入的记录数是 13 亿多
而 task=000000 的 counter 如下,其输入记录数是 230 多亿。是其他任务的 100 多倍:
3、为什么会产生数据倾斜问题
大部分数据倾斜问题是由于key 分布不均匀造成的。
4、如何定位数据倾斜
4.1、确定任务卡住的stage
-
可以通过 jobname 确定 stage,因为Hive的默认jobname会带上名称会带上stage阶段,如下图为Stage-1。
-
找到执行特别慢的那个task,搜索 “CommonJoinOperator: JOIN struct” 。Hive在做join的时候,会把join的key打印到日志中,如下:
上图中的关键信息是:struct<_col0:string, _col1:string, _col3:string>
这时候,需要参考该 SQL 的执行计划。通过参考执行计划,可以断定该阶段为 Stage-4 阶段:
4.2、确定 SQL执行代码
确定了执行阶段,即 stage。通过执行计划,则可以判断出是执行哪段代码时出现了倾斜。还是从此图,这个 stage 中进行连接操作的表别名是 d:
就可以推测出是在执行下面红框中代码时出现了数据倾斜,因为这行的表的别名是 d:
5、解决方案
场景一、两表join时关联字段为null
如日志中,常会有信息丢失的问题,比如全网日志中的user_id,如果取其中的user_id和hy_users表关联,会碰到数据倾斜的问题
解决方法1: 先过滤掉值为null的key,不参与关联,之后再单独join后进行合并
Select * From log a
Join hy_users b
On a.user_id is not null
And a.user_id = b.user_id
Union all
Select * from log a
where a.user_id is null;
解决方法2 :加盐,给值为null的key赋新值
Select *
from log a
left outer join hy_users b
on case when a.user_id is null then concat(‘xxxx’,rand() ) else a.user_id end = b.user_id;
因为这些数据本来就是无效数据,根本不会出现在结果表中,所以,这样处理user_id(由一个字符串(比如我自己给它定一个 hive)加上一个随机数),它也无法关联的,不影响最终结果。
场景二、不同数据类型关联产生数据倾斜
如一张表s8的日志,每个商品一条记录,要和商品表关联。但关联却碰到倾斜的问题。s8的日志中有字符串商品id,也有数字的商品id,类型是string的,但商品中的数字id是bigint的。猜测问题的原因是把s8的商品id转成数字id做hash来分配reduce,所以字符串id的s8日志,都到一个reduce上了,解决的方法验证了这个猜测
解决方法: 把数字类型转换成字符串类型
Select * from s8_log a
Left outer join r_auction_auctions b
On a.auction_id = cast(b.auction_id as string);
场景三、join时产生数据倾斜
-
1、转换为MapJoin
小表join大表时使用MapJoin,顾名思义,就是在Map阶段进行表之间的连接 MapJoin通常用于一个很小的表和一个大表进行join的场景。而不需要进入到Reduce阶段才进行连接。这样就节省了在Shuffle阶段时要进行的大量数据传输。从而起到了优化作业的作用。
MapJoin简单说就是在Map阶段将小表读入内存,顺序扫描大表完成Join。
上图是Hive MapJoin的原理图(引用Facebook工程师Liyin Tang的一篇介绍Join优化的slice)从图中可以看出MapJoin分为两个阶段:
1.通过MapReduce Local Task,将小表读入内存,生成HashTableFiles上传至Distributed Cache中,这里会对HashTableFiles进行压缩
2.MapReduce Job在Map阶段,每个Mapper从Distributed Cache读取HashTableFiles到内存中,顺序扫描大表,在Map阶段直接进行Join,将数据传递给下一个MapReduce任务。 -
2、count distinct大量相同特殊值
count distinct 时,将值为空的情况单独处理,如果是计算count distinct,可以不用处理,直接过滤,在最后结果中加1。如果还有其他计算,需要进行group by,可以先将值为空的记录单独处理,再和其他计算结果进行union。
select cast(count(distinct user_id)+1 as bigint) as user_cnt
from tab_a
where user_id is not null and user_id <> ''
- 3、通过设置参数调节
hive.map.aggr = true
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中),最后完成最终的聚合操作。