简述
分区是hive存放数据的一种方式,将列值作为目录来存放数据,就是一个分区,可以有多列。
这样查询时使用分区列进行过滤,只需根据列值直接扫描对应目录下的数据,不扫描不关心的分区,快速定位,提高查询效率。
hive的分区有两种类型:
- 静态分区SP(Static Partitioning)
- 动态分区DP(Dynamic Partitioning)
对于静态分区,表的分区数量和分区值是固定的。新增分区或者是加载分区数据时,需要提前指定分区名。
对于动态分区,分区的值是不确定的,会根据数据自动的创建新的分区。
一、静态分区
如上所述,静态分区的使用场景主要是分区的数量是确定的。例如日志流水数据中使用日期作为分区字段,通常在写入之前就已经确定了是哪个分区。
1.单分区建表
create table if not exists day_log(
uid bigint,
uname string,
action string
) comment '用户动作流水记录'
partitioned by(ymd string comment '日期格式yyyyMMdd')
row format delimited fields terminated by '\t';
2.加载数据到指定分区
load data local inpath '/user/hive/data/day_log.txt'
into table day_log paritition(ymd='20220803')
3.创建具有多个分区的表
create table if not exists day_log(
uid bigint,
uname string,
action string
) comment '用户动作流水记录'
partitioned by(year string,month string,day string)
row format delimited fields terminated by '\t';
4.加载数据
load data local inpath '/user/hive/data/day_log.txt'
into table day_log paritition(year='2022',month='08',day='02')
但通常我们写入分区数据是通过计算SQL结果直接写入,并不是从外部文件load进来的。示例如下:
insert overwrite table day_log partition (year='2022',month='08',day='02')
select uid,uname,action from (
xxxxxx
)
二、动态分区
所谓动态分区,分区的值是不确定的,分区的数量是不确定,皆由加载数据确定。
生产环境中,动态分区一般常用于创建新表后,需要一次性加载历史数据。
1.创建临时表
-- 创建临时表
create table if not exists tmp (
uid int,
commentid bigint,
recommentid bigint,
year int,
month int,
day int
)
row format delimited fields terminated by '\t';
-- 加载数据到临时表
load data local inpath 'user/hive/data/tmp.txt' into table tmp;
2.创建动态分区表
-- 创建动态分区表
create table if not exists dp_tmp(
uid int,
commentid bigint,
recommentid bigint
)
partitioned by (year string,month string,day string)
row format delimited fields terminated by '\t';
-- 写入数据到分区表
-- 参数为开启动态分区
set hive.exec.dynamic.partition=true;
insert overwrite table dp_tmp partition(year,month,day)
select uid,commentid,recommentid,year,month,day from tmp;
执行上述写入语句会报错:
FAILED: SemanticException [Error 10096]: Dynamic partition strict mode requires at least one static partition column. To turn this off set hive.exec.dynamic.partition.mode=nonstrict
看报错信息:动态分区严格模式至少需要一个静态分区列。关闭它,设置参数
set hive.exec.dynamic.partition.mode=nonstrict
下文介绍hive相关参数作用
3.严格模式
参数hive.exec.dynamic.partition.mode
表示动态分区的模式。
默认是strict
,也就是严格模式,表示必须指定至少一个分区为静态分区
nonstrict
模式,即非严格模式,表示允许所有的分区字段都可以使用动态分区
严格模式
-- 至少需要指定一个静态分区列
-- 开启动态分区
set hive.exec.dynamic.partition=true;
insert overwrite table dp_tmp partition(year='2022',month,day)
select uid,commentid,recommentid,month,day from tmp;
4.非严格模式
set hive.exec.dynamic.partition=true;
-- 允许所有的分区字段都可以使用动态分区,兼容严格模式
-- 更改动态分区模式为非严格模式
set hive.exec.dynamic.partition.mode=nonstrict;
insert overwrite table dp_tmp partition(year,month,day)
select uid,commentid,recommentid,month,day from tmp;
通常情况下,我们使用动态分区,为非严格模式:
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
三、静态分区和动态分区的区别
两种分区模式根据定义就可看出来明显区别,这里单列一下:
静态分区(Static Partitioning) | 动态分区(Dynamic Partitioning) | |
分区创建 | 数据插入分区之前,需要手动指定创建每个分区 | 根据表的输入数据动态创建分区 |
适用场景 | 需要提前知道所有分区。适用于分区定义得早且数量少的用例,常见为插入某一个指定分区 | 有很多分区,无法提前预估新分区,动态分区是合适的 |
另外动态分区的值是MapReduce
任务在reduce
运行阶段确定的,也就是所有的记录都会distribute by
,相同字段(分区字段)的map
输出会发到同一个reduce
节点去处理,如果数据量大,这是一个很弱的运行性能。
而静态分区在编译阶段就确定了,不需要reduce
任务处理。所以如果实际业务场景静态分区能解决的,尽量使用静态分区即可。
四、分区使用注意事项
1.hive分区参数及作用
hive表中的分区作用主要是使数据按照分区目录存储在hdfs上,查询只要针对指定的目录集合进行查询,避免全局查找,这样提高了查询性能。
hive的分区需要合理使用,过多的分区目录和文件对于集群Namenode
服务是有性能压力的,Namenode
需要将大量的元数据信息保存在内存中。如果报错,会造成Namenode
不可用。
一次查询表里有太多分区,会使得查询文件过大,也会造成Metastore
服务出现OOM
报错,报错信息显示Metastore
不可用。
hive为了避免因为异常产生大量分区,导致上述问题,本身是默认动态分区关闭,同时对生成动态分区的数量也做了一定限制。
通过手动参数设置可以改变系统默认值,具体hive默认参数以及SQL执行配置参数(不同版本默认参数有一定差异)如下:
-- Hive默认配置值
-- 开启或关闭动态分区
hive.exec.dynamic.partition=false;
-- 设置为nonstrict模式,让所有分区都动态配置,否则至少需要指定一个分区值
hive.exec.dynamic.partition.mode=strict;
-- 能被mapper或reducer创建的最大动态分区数,超出而报错
hive.exec.max.dynamic.partitions.pernode=100;
-- 一条带有动态分区SQL语句所能创建的最大动态分区总数,超过则报错
hive.exec.max.dynamic.partitions=1000;
-- 全局能被创建文件数目的最大值,通过Hadoop计数器跟踪,若超过则报错
hive.exec.max.created.files=100000;
-- 根据个人需要配置
-- 设置动态分区开启
set hive.exec.dynamic.partition=true;
-- 设置为非严格模式
set hive.exec.dynamic.partition.mode=nonstrict;
-- 设置每个节点创建最大分区数
set hive.exec.max.dynamic.partitions.pernode=1000;
-- 设置执行SQL创建最大分区数
set hive.exec.max.dynamic.partitions=10000;
-- 设置全局被创建文件最大值
set hive.exec.max.created.files=1000000;
在执行hiveSQL的时候如果动态分区数量或文件数任何一个超过集群默认就会产生报错:
ERROR [LocalJobRunner Map Task Executor #0]:mr.ExecMapper (ExecMapper.java:map(171)) - org.apache.hadoop.hive.ql.metadata.HiveException: Hive Runtime Error while processing row ....
Caused by: org.apache.hadoop.hive.ql.metadata.HiveFatalException: [Error 20004]: Fatal error occurred when node tried to create too many dynamic partitions. The maximum number of dynamic partitions is controlled by hive.exec.max.dynamic.partitions and hive.exec.max.dynamic.partitions.pernode. Maximum was set to: 256... 10 more
集群会kill任务。为了解决报错,我们通常将三个参数调大。但是也需要用户对自己的Hive表的分区数量进行合理规划,避免过多的分区。
2.分区常见注意事项
a. 尽量不要使用动态分区,因为动态分区的时候,将会为每一个分区分配reducer数量,当分区数量多的时候,reducer数量将会增加,对服务器是一种灾难。
b. 动态分区和静态分区的区别,静态分区不管有没有数据都会创建指定分区,动态分区是有结果集将创建,否则不创建。
c. hive动态分区的严格模式和hive严格模式是不同的。
hive提供的严格模式简述:
hive提供的严格模式,为了组织用户不小心提交恶意SQLhive.mapred.mode=nostrict : strict
如果该模式值为strict,将会阻止一下三种查询:
a.对分区表查询,where条件中过滤字段没有分区字段;
b.笛卡尔积join查询,join查询语句中不带on条件或者where条件;
c.对order by查询,有order by的查询不太limit语句。
3.一些异常分区处理
a.默认分区
如果动态分区列输入的值为NULL或空字符串,则hive将该行放入一个特殊分区,分区名称由参数hive.exec.default.partition.name
控制。
默认值为__HIVE_DEFAULT_PARTITION__
。可以通过查看表分区命令进行查看:
show partitions 'table';
-- ymd=__HIVE_DEFAULT_PARTITION__
清理该分区使用正常删除分区语句即可。对分区的操作命令详见上篇文章。
b.乱码分区
表分区字段处理不当可能会造成乱码分区,主要是由于转译编码原因造成。例如:
sp_test=r_ready%3D91;r_load%3D351
原因是Hive会自动对一些UTF-8字符编码成Unicode(类似网址中中文字符和一些特殊字符的编码处理)。此处%3D解码后是'='。可以使用在线转换进行解码:https://www.matools.com/code-convert-utf8。
最后使用解码后的字段即可(注意分号转义):
alter table dpdw_traffic_base drop partition(sp_test='r_ready=91\;r_load=351');