hive复习题、面试题

这里是面试常考题

1. hive架构原理

hive复习题、面试题_第1张图片
用户接口:Client

CLI(hive shell)、JDBC/ODBC(java访问hive)、WEBUI(浏览器访问hive)

元数据:Metastore

包括表名、表所属的数据库、表的拥有者、列/分区字段、表的类型、表数据所在的目录等(自带个derby数据库,推荐配置到MySQL)

底层存储:HDFS

使用HDFS进行存储,使用MapReduce计算

驱动器:Driver

解析器(SQL Parser):将SQL字符串转换成抽象语法树AST,并对语法树进行语法分析,如:SQL语法、表/字符是否存在
编译期(Physical Plan):将AST编译生成逻辑执行计划
优化器(Query Optimizer):对逻辑执行计划进行优化
执行器(Execution):把逻辑执行计算转换成运行的物理计划,即MR/Spark

HQL转换为MR任务流程

  1. 进入程序,利用Antlr框架定义HQL的语法规则,对HQL完成词法语法解析,将HQL转换为AST(抽象语法树);

  2. 遍历AST,抽象出查询的基本组成单元QueryBlock(查询块),可以理解为最小的查询执行单元;

  3. 遍历QueryBlock,将其转换为OperatorTree(操作树,也就是逻辑执行计划),可以理解为不可拆分的一个逻辑执行单元;

  4. 使用逻辑优化器对OperatorTree(操作树)进行逻辑优化。例如合并不必要的ReduceSinkOperator,减少Shuffle数据量;

  5. 遍历OperatorTree,转换为TaskTree。也就是翻译为MR任务的流程,将逻辑执行计划转换为物理执行计划;

  6. 使用物理优化器对TaskTree进行物理优化;

  7. 生成最终的执行计划,提交任务到Hadoop集群运行。

一句话:

  1. hiveSQL被parser(解析)成AST(抽象语法树)
  2. AST转化成query block(查询块)
  3. query block被转化成operator tree(操作树)
  4. 优化operator tree
  5. operator tree转化成mr任务
  6. 优化成最终的mr任务

2. hive运行机制

hive复习题、面试题_第2张图片

3. 数据上传

3.1 方法一 put:

hadoop dfs -put xxx---本地数据的绝对路径 /user/hive/warehouse/xx--这个xx是你自己创建的数据库

3.2 方法二 load:

load data [local] inpath path [overwrite] into table table_name [partition(p1=v1,...)]

hive复习题、面试题_第3张图片

4. 数据导出

insert overwrite local directory 'xxx--本地路径' select 查询语句;

5. 创表相关

5.1 分割符

Hive 表的默认分割符是:\001
以后创建表肯定要做分割的,所以需要在创表语句之后加上。
row format delimited fields terminated by ‘xxx–你需要按照什么去分割’

5.2 查看数据库更多详细信息:desc

desc database extended xxx--你需要删除的数据库名字

5.3 删除数据库:drop

drop database xxx--你需要删除的数据库名字

6. 数据库表的操作

6.1 内部表

6.1.1 创建内部表表

create [external] table [if not exists] table_name(
col_name data_type [comment '字段描述信息']
col_name data_type [comment '字段描述信息']
[comment '表的描述信息']
[partitioned by (col_name data_type),...]
[clustered by (col_name,col_name,...]
[sorted by (col_name [asc|desc],...) into num_buckets buckets]
[row format row_format]
[storted as ...]
[location '指定表的路径']
)

说明:
create table
创建一个指定名字的表。如果相同名字的表已经存在,则抛出异常;用户可以用 IF NOT EXISTS 选项来忽略这个一场
external
可以让用户创建一个外部表,在建表的同时指定一个指向实际数据的路径 (LOCATION),Hive 创建内部表时,会将数据移动到数据仓库指向的路径;若创建外部表,仅记录数据所在的路径,不对数据的位置做任何改变。在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据。
comment
表示注释,默认不能使用中文
partitioned by
表示使用表分区,一个表可以拥有一个或者多个分区,每一个分区单独存在一个目录下
clustered by
对于每一个表分文件,Hive 可以进行组织成桶,也就是说桶是更为细粒度的数据范围划分。Hive 也是针对某一列进行桶的组织
sorted by
指定排序字段和排序规则
row format
指定表文件字段分隔符
storted as
指定表文件的存储格式。常用格式:SEQUENCEFILE,TEXTFILE,RCFILE, 如果数据是纯文本,可以使用 STORED AS TEXTFILE。如果数据需要压缩,使用 storted as SEQUENCEFILE。
location
指定表文件的存储路径

6.1.2 创建表并且指定文件的存放路径

create table if not exists table_name(id int,name string,...) 
row format delimited fields terminated by ',' 
location 'xxx--你需要存放的路径'

6.1.3 根据查询结果创建表

create table table_name as select * from table_name1; # 通过复制表结构和表内容创建新表

6.1.4 根据已经存在的表结构创建表

create table table_name like table_name1; # 只会创建出表的结构,并不会同时创建表中的数据

6.1.5 查看表的详细信息

desc formatted xxx--你表的名字

6.1.6 删除表

drop table xxx--你表的名字

其实是将数据移动到了回收站。

6.2 外部表

外部表说明
外部表因为是指定其他的 hdfs 路径的数据加载到表当中来,所以 hive 表会认为自己不完全独占这份数据,所以删除 hive 表的时候,数据仍然存放在 hdfs 当中,不会删掉。

内部表和外部表的使用场景
例如:每天需要将收集到的网站日志定期流入 HDFS 文本文件中。然后在这个基础上对数据做大量分析,而这个数据可能要对很多部门进行共享,那么这个时候我们就需要用到外部表。而每个部门经过分析之后得到了属于自己的一些结果,那么我们就可以将生成的一些中间表,结果表使用内部表存储。数据通过 SELECT+INSERT 进入内部表。

创建外部表的时候只需要加上 external 关键字即可。

此时创建的这张表就是外部表。

6.2.1 查询表的类型

desc formatted xx--你的表的名字

6.2.2 修改表为外部表

alter table xx--你需要修改表的名字 set tblproperties('EXTERNAL'='TRUE');

此时就能很直观的看到已经修改为外部表了。

注意:’EXTERNAL’=’TRUE’必须是大写!!!

6.3 分区表的操作

在大数据中,最常用的一种思想就是分治,我们可以把大的文件切割划分成一个个的小的文件,这样每次操作一个小的文件就会很容易了,同样的道理,在 hive 当中也是支持这种思想的,就是我们可以把大的数据,按照每月,或者天进行切分成一个个的小的文件,存放在不同的文件夹中。

6.3.1 关键字:partitioned by

例如:

create table fst(id int,name string) partitioned by (xxx--你要以什么作为分区) 
row format delimited fields terminated by '\t';

6.3.2 加载数据到分区

因为之前已经分过区了,所以加载数据进去的时候需要指明以什么作为分区。

load data local inpath '/usr/local/data/teacher' into table fst 
partition (month='20210928');

6.3.2 增加分区

alter table xx--你的表 add partition (xxx--你的分区)

6.3.3 查看分区

show partitions xxx--你自己的数据表

6.3.4 删除分区

alter table xxx--你自己的表 drop partition(xxx--你刚刚创的分区);

6.3.5 修复表

当表与数据文件之间的映射关系没有建立起来时,可以对表进行修复,重新建立映射关系。

msck repair table xxx--你自己表的名称;

6.4 动态分区

动态分区:根据数据中某几列的不同的取值划分不同的分区。但是在 Hive 中是默认不开启动态分区的。我们要想进行动态分区,就需要开启它。

6.4.1 开启 Hive 的动态分区功能

set hive.exec.dynamic.partition=true;# 表示开启动态分区
set hive.exec.dynamic.partition.mode=nostrict;# 表示动态分区模式:strict(需要配合静态分区一起使用)、nostrict
set hive.exec.max.dynamic.partitions.pernode=1000;# 表示支持的最大的分区数量为1000,可以根据业务自己调整

6.5 分桶表操作

桶表是对数据进行哈希取值,然后放到不同文件中存储。数据加载到桶表时,会对字段取 hash 值,然后与桶的数量取模。把数据放到对应的文件中。物理上,每个桶就是表(或分区)目录里的一个文件,一个作业产生的桶(输出文件)和 reduce 任务个数相同。桶表专门用于抽样查询,是很专业性的,不是日常用来存储数据的表,需要抽样查询时,才创建和使用桶表。

分区针对的是数据的存储路径,分桶针对的是数据文件

分桶,就是将数据按照指定的字段进行划分到多个文件当中去,分桶就是 MapReduce 中的分区。

Hive 默认的是关闭了分桶功能,此时我们要想进行分桶操作,就要开启它。

6.5.1 开启 Hive 的分桶功能

set hive.enforce.bucketing=true;

6.5.2 设置 Reduce 个数

set mapreduce.job.reduce=3;

6.5.3 创建分桶表

关键字:clustered by

create table course(c_id string,c_name string,t_id string) 
clustered by (c_id) into 3 buckets 
row format delimited fields terminated by '\t';

注意:桶表中的数据加载,由于桶表的数据加载通过 hdfs dfs -put 文件或者通过 load data 均不好使,只能通过 insert overwrite。

创建普通表,并通过 insert overwrite 的方式将普通表通过查询的方式加载到桶表当中去。

6.5.3.1 创建普通表
create table course_common(c_id string,c_name string,t_id string) row format delimited fields terminated by '\t';
6.5.3.2 普通表中加载数据
load data local inpath 'xxx--你自己数据的路径' into table course_common;
6.5.3.3 通过 insert overwrite 给桶表中加载数据
insert overwrite table course select * from course_common cluster by (c_id);

6.6 修改表的结构

6.6.1 重命名

alter table old_table_name rename to new_table_name;

6.6.2 添加列

alter table xxx--你需要添加的列的表 add columns (xxx--这是你需要添加的字段)

6.6.3 更新列

alter table xxx---你需要修改的列的表 change column x--原来列的名字 xx--新的列名字 xxx--新的列属性

7. hive语法

其余与mysql相似不过多赘述

7.1 关于排序

7.1.1 order by

会对输入做全局排序,因此只有一个 reducer,会导致当输入规模较大时,需要较长的计算时间。
其实就是MapReduce的全局排序,在hive中体现为order by对应一个reduce,因为站在MapReduce角度全局排序必须输出一个文件因此必须只有一个reduce。
当使用order by排序是会启动一个reduce,那么当手动设置reduce个数最终会启动几个reduce呢?
答案是:提交一个job,启动一个reduce,发现无论设置多少个reduce个数全局排序就只会启动一个redcue。

7.1.2 sort by

不是全局排序,其在数据进入 reducer 前完成排序。因此,如果使用sort by 进行排序,并且设置 mapred.reduce.task>1, 则 sort by 只保证每个 reducer 的输出有序,不保证全局有序。
set mapreduce.job.reduce=3;可以设置reduce个数。
但问题是在MapReduce中分区默认为HashPartitioner根据key的hash和reduce个数取余,那么hive是怎么实现分区呢?按照哪(几)个字段实现分区?从官方文档可以找到答案,没有指定分区的hql来说hive通过随机值的形式分区。
为什么?当我们没有指定字段分区时,若hive还按照字段进行分区万一导致数据倾斜问题该找谁呢?所以hive通过随机加载的形式尽可能减少数据倾斜的发生。

7.1.3 distribute by

distribute by (字段) 根据指定的字段将数据分到不同的 reducer,且分发算法是 hash 散列。类似 MR 中的 partition,进行分区,结合 sort by 使用。

按照部门id分区,分区内按照薪资升序排序

select * from emp distribute by deptno sort by sal;

7.1.4 cluster by

cluster by (字段) 除了具有 distribute by 的功能外,还会对该字段进行排序。
因此,如果 distribute 和 sort 字段是同一个时,此时,cluster by = distribute by + sort by。
那么有人就问了,对一个字段分区又排序的意义可在?当我们数据有很多时,分区数少但该字段类型有很多种,因此就会有很多字段进入同一个分区,在对同一个分区按照该字段排序。
cluster by 有什么意义?

7.2 关于函数

7.2.1 空字段赋值

NVL:给值为 NULL 的数据赋值,它的格式是 NVL (string1,replace_with)。它的功能是如果 string1 为 NULL,则 NVL 函数返回 replace_with,否则返回 string1 的值,如果两个参数都为 NULL,则返回 NULL。

7.2.2 时间类

hive复习题、面试题_第4张图片
hive复习题、面试题_第5张图片
hive复习题、面试题_第6张图片

7.2.3 行转列

CONCAT (string A/col, string B/col…): 返回输入字符串连接后的结果,支持任意个输入字符串;

CONCAT_WS (separator, str1, str2,…): 它是一个特殊形式的 CONCAT ()。第一个参数剩余参数间的分隔符。分隔符可以是与剩余参数一样的字符串。如果分隔符是 NULL,返回值也将为 NULL。这个函数会跳过分隔符参数后的任何 NULL 和空字符串。分隔符将被加到被连接的字符串之间;

COLLECT_SET (col): 函数只接受基本数据类型,它的主要作用是将某字段的值进行去重汇总,产生 array 类型字段。

7.2.4 列转行(膨胀函数)

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

LATERAL VIEW

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

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

7.2.5 窗口函数

OVER ():指定分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化;

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 类型

7.2.6 rank

RANK () 排序相同时会重复,总数不会变

DENSE_RANK () 排序相同时会重复,总数会减少

ROW_NUMBER () 会根据顺序计算

7.2.7 内置函数

常用的内置函数如下(共216个)
hive复习题、面试题_第7张图片
hive复习题、面试题_第8张图片
在这里插入图片描述

hive函数大全

8.自定义函数

UDF:单行进入,单行输出
UDAF:多行进入,单行输出
UDTF:单行输入,多行输出

8.1 UDF

  1. 继承org.apache.hadoop.hive.ql.exec.UDF
  2. 实现evaluate函数,该函数支持重载
  3. hive中添加jar包
  4. 创建函数
  5. 使用函数
  6. 删除函数
  7. 添加依赖
<dependency>
	<groupId>org.apache.hivegroupId>
	<artifactId>hive-execartifactId>
	<version>3.1.2version>
dependency>

  1. 继承org.apache.hadoop.hive.ql.exec.UDF
package hive.udf;
 
import org.apache.hadoop.hive.ql.exec.UDF;
 
@SuppressWarnings("deprecation")
public class UDFLength extends UDF {
 
}

  1. 实现evaluate函数,该函数支持重载
package hive.udf;
 
import org.apache.hadoop.hive.ql.exec.UDF;
 
@SuppressWarnings("deprecation")
public class UDFLength extends UDF {
    public int evaluate(String str) {
        return str.length();
    }
}

  1. hive中添加jar包
hive (hive)> add jar /usr/local/soft/hive-3.2.1/lib/hadoop-1.0-SNAPSHOT.jar;
Added [/usr/local/soft/hive-3.2.1/lib/hadoop-1.0-SNAPSHOT.jar] to class path
Added resources: [/usr/local/soft/hive-3.2.1/lib/hadoop-1.0-SNAPSHOT.jar]
  1. 创建函数
    hive中自定义函数创建时可以分为临时函数和永久函数,对于临时函数仅在当前session,当前数据库起作用。

标准语法

create [temporary] function fun_name as 'package.class';

设置函数名getlength

hive (hive)> create temporary function getlength as 'hive.udf.UDFLength';
OK
Time taken: 0.047 seconds

  1. 使用函数
hive (hive)> select *,getlength(st_name) from student;
OK
student.st_id   student.st_name student.st_sex  student.st_age  student.st_dept _c1
10001   郑楠    男      20      ES      2
10002   李娜    女      19      IS      2
Time taken: 0.145 seconds, Fetched: 2 row(s)

hive (hive)> desc function extended getlength;
OK
tab_name
There is no documentation for function 'getlength'
Function class:hive.udf.UDFLength
Function type:TEMPORARY
Time taken: 0.017 seconds, Fetched: 3 row(s)

  1. 删除函数
hive (hive)> drop function if exists getlength;
OK
Time taken: 0.648 seconds

8.2 GenericUDF

UDF类已经过时,hive推荐使用GenericUDF,该类支持更多的数据类型且效率更高

package hive.udf;
 
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
 
public class GenericUDFLength extends GenericUDF {
    @Override
    public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
        return null;
    }
 
    @Override
    public Object evaluate(DeferredObject[] arguments) throws HiveException {
        return null;
    }
 
    @Override
    public String getDisplayString(String[] children) {
        return null;
    }
}

initialize初始化,可以进行参数校验,对象实例化等等
evaluate业务逻辑
getDisplayString显示函数的帮助信息

这个evaluate显然是优于UDF的,可是让我们函数接受任意多任意类型的值。

package hive.udf;
 
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.io.IntWritable;
 
public class GenericUDFLength extends GenericUDF {
    IntWritable result = new IntWritable();
 
    @Override
    public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
        return PrimitiveObjectInspectorFactory.writableIntObjectInspector;
    }
 
    @Override
    public IntWritable evaluate(DeferredObject[] arguments) throws HiveException {
        if (arguments.length > 1) {
            System.out.println("该函数暂时不支持多个参数");
            return null;
        }
        result.set(arguments[0].get().toString().length());
        return result;
    }
 
    @Override
    public String getDisplayString(String[] children) {
        return "获取字符串长度";
    }
}

返回了长度

hive (default)> create temporary function getlength as 'hive.udf.GenericUDFLength';
OK
Time taken: 0.519 seconds
hive (default)> select getlength('123');
OK
_c0
3
Time taken: 3.072 seconds, Fetched: 1 row(s)

8.3 UTAF

  1. 函数类继承AbstractGenericUDAFResolver、计算类实现GenericUDAFEvaluator接口
  2. 实现UDAFEvaluator接口的init、iterate、terminatePartial、merge、terminate
  3. init初始化
  4. iterate接收传入参数,进行内部迭代
  5. terminatePartial返回iterate后的数据
  6. merge接收terminatePartial返回结果,进行merge操作
  7. terminate返回最终的结果
import org.apache.hadoop.hive.ql.exec.UDF;

public class HiveUDF01 extends UDF {
    public String evaluate(String str1,String str2){
        String s = str1+"---"+str2;
        return s;
    }
}

发现有多个参数的时候,也是可以只输出一个结果,所以 UDAF(多进一出)就不常用

8.4UDTF

  1. 创建一个类继承GenericUDTF
  2. 实现initialize、process、close方法
package hive.udtf;
 
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.io.Text;
 
import java.util.ArrayList;
import java.util.List;
 
public class UDTFSplit extends GenericUDTF {
 
   //保存字符串分割后的数据,用于多行输出
   Text result = new Text();
 
   public StructObjectInspector initialize(StructObjectInspector argOIs)
           throws UDFArgumentException {
      List<String> fieldNames = new ArrayList<>();
      List<ObjectInspector> fieldOIs = new ArrayList<>();
      // 这个字段最终会显示为结果的字段名
      fieldNames.add("结果");
      // 申明函数返回值类型
      fieldOIs.add(PrimitiveObjectInspectorFactory.writableStringObjectInspector);
      return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
   }
 
 
   @Override
   public void process(Object[] args) throws HiveException {
      if (args.length != 2) {
         throw new RuntimeException("参数个数不匹配");
      }
      //第一个参数为待分割的字符串
      String[] split = args[0].toString().split(args[1].toString());
      for (String value : split) {
         result.set(value);
         //类似context.write()
         forward(result);
      }
   }
 
   @Override
   public void close() throws HiveException {
      //可以在这里实现一些关流操作
   }
}

使用

hive (default)> select udtfsplit('hello_world_hello_hive','_');
        OK
        结果
        hello
        world
        hello
        hive
        Time taken: 2.406 seconds, Fetched: 4 row(s)

第二个例子

import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;

import java.lang.reflect.Array;
import java.util.ArrayList;

public class HiveUDTF extends GenericUDTF {
    //1.3.1之后不需要写
    //初始化:指定输出的数量和输出的格式
    @Override
    public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
        //指定输出多少列(操作列的数量和名称)
        ArrayList<String> fieldName = new ArrayList<String>();
        //操作列的类型
        ArrayList<ObjectInspector> fieldObj = new ArrayList<ObjectInspector>();//检测当前给的是什么类型
        //输出两列
        //第一列
        fieldName.add("x");
        fieldObj.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);//检查给的是不是String类型
        //第二列
        fieldName.add("y");
        fieldObj.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
        return ObjectInspectorFactory.getStandardStructObjectInspector(fieldName,fieldObj);

    }

    //假设进来的格式为:("k1:v1,k2:v2,k3:v3")
    @Override
    public void process(Object[] objects) throws HiveException {
        String s = objects[0].toString();//这里的参数0,指的就是上面进来的格式
        //切分出来每行数据
        String[] rows = s.split(",");
        for (String row : rows) {
            String[] split = row.split(":");
            //通过forward()输出
            forward(split);
        }
    }

    @Override
    public void close() {

    }
}
hive> create temporary function fun as 'HiveUDTF';
hive> select fun('k1:v1,k2:v2,k3:v3');
OK
k1	v1
k2	v2
k3	v3
Time taken: 2.406 seconds, Fetched: 4 row(s)

其他

hive 执行过程中数据倾斜问题

原文:

版权声明:本文为CSDN博主「寒枫__梦」的原创文章,遵循CC 4.0
BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41408329/article/details/106116543

1.描述:
数据倾斜主要表现在,MR程序执行过程中,reduce节点大部分执行完毕,但是有一个或者少数几个节点运行很慢,导致整个程序的处理时间很长。这是因为该reduce处理节点对应的key对应数据条数远超于其他key导致该节点处理数据量太大而不能和其他节点同步完成执行。简而言之,解释同一key对应数据条数太多导致。常见有空值key。

2.原因:
2.1、key分布不均匀。
2.2、业务数据本身的原因。
2.3、建表考虑不周。
2.4、某些SQL本身就有数据倾斜。

3.解决方式:
3.1、给key一个随机值,打乱key,进行第一步操作,然后第二步去掉随机值再处理。
3.2、参数调节:
hive.map.aggr = true Map 端部分聚合,相当于Combiner。
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 中),最后完成最终的聚合操作。
3.3、SQL语句调节:  
3.3.1 大小表Join:使用map join让小的维度表(1000条以下的记录条数) 先进内存。在map端完成reduce.
3.3.2 大表Join大表:把空值的key变成一个字符串加上随机数,把倾斜的数据分到不同的reduce上,由于null值关联不上,处理后并不影响最终结果。
3.3.3 count distinct大量相同特殊值:count distinct时,将值为空的情况单独处理,如果是计算count distinct,可以不用处理,直接过滤,在最后结果中加1。如果还有其他计算,需要进行group by,可以先将值为空的记录单独处理,再和其他计算结果进行union。
3.3.4 group by维度过小:采用sum() group by的方式来替换count(distinct)完成计算。
3.3.5 特殊情况特殊处理:在业务逻辑优化效果的不大情况下,有些时候是可以将倾斜的数据单独拿出来处理。最后union回去。
3.4、map和reduce优化。
   3.4.1 当出现小文件过多,需要合并小文件。可以通过set hive.merge.mapfiles=true来解决。
3.4.2 单个文件大小稍稍大于配置的block块的大写,此时需要适当增加map的个数。解决方法:set mapred.map.tasks个数
3.4.3 文件大小适中,但map端计算量非常大,如select id,count(*),sum(case when…),sum(case when…)…需要增加map个数。解决方法:set mapred.map.tasks个数,set mapred.reduce.tasks个数

4.Hive的优化:
4.1、配置fetch抓取:修改配置文件hive.fetch.task.conversion为more,修改之后全局查找和字段查找以及limit都不会触发MR任务。
4.2、善用本地模式:大多数的Hadoop Job要求Hadoop提供完整的可扩展性来触发大数据集,不过有时候hive的输入数据量非常的小,这样的情况下可能触发任务的事件比执行的事件还长,我们就可以通过本地模式把小量的数据放到本地上面计算。
4.3、Group by:默认情况下,map阶段同一key的数据会发给一个reduce,当一个key数据过大就会倾斜,并不是所有的聚合操作都需要reduce端完成,很多聚合操作都可以现在map端进行部分聚合,最后在reduce端得出最终结果。
4.3.1 开启在map端聚合:hive.map.aggr = true。
4.3.2 在map端进行聚合操作的条目数:hive.groupby.mapaggr.checkinterval = 100000。
4.3.3 有数据倾斜的时候进行负载均衡:hive.groupby.skewindata = true。
4.4、行列过滤:列处理:只拿自己需要的列,如果有,尽量使用分区过滤。行处理:在分区裁剪的时候,当使用外关联的时候,先完全关联再过滤。
4.5、动态分区调整:(慎用)
4.5.1 开启动态分区:hive.exec.dynamic.partition=true。
4.5.2 设置为非严格模式:hive.exec.dynamic.partiton.mode = nostrict。
4.5.3 实操:创建分区表、加载数据到分区表中、创建目标分区、设置动态分区、查看目标分区表的分区情况。
4.6、小文件进行合并:在map执行之前合并小文件,减少map的数量,设置 set hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat。
4.7、调整map的数量和reduce的数量。
4.8、并行执行:在Hive中可能有很多个阶段,不一定是一个阶段,这些阶段并非相互依赖的。然后这些阶段可以并行完成,设置并行:set hive.exec.parallel = true。
4.9、JVM的重用,缺点是task槽会被占用,一直要等到任务完成才会释放。

你可能感兴趣的:(hive,hive,big,data)