面试官问我,什么是hive的静态分区和动态分区,这题我会呀。
分区是hive存放数据的一种方式,将列值作为目录来存放数据,就是一个分区,可以有多列。
这样查询时使用分区列进行过滤,只需根据列值直接扫描对应目录下的数据,不扫描不关心的分区,快速定位,提高查询效率。
hive的分区有两种类型:
对于静态分区,表的分区数量和分区值是固定的。新增分区或者是加载分区数据时,需要提前指定分区名。
对于动态分区,分区的值是不确定的,会根据数据自动的创建新的分区。
如上所述,静态分区的使用场景主要是分区的数量是确定的。例如日志流水数据中使用日期作为分区字段,通常在写入之前就已经确定了是哪个分区。
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';
load data local inpath '/user/hive/data/day_log.txt'
into table day_log paritition(ymd='20220803')
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';
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
)
所谓动态分区,分区的值是不确定的,分区的数量是不确定,皆由加载数据确定。
生产环境中,动态分区一般常用于创建新表后,需要一次性加载历史数据。
-- 创建临时表
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;
-- 创建动态分区表
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相关参数作用
参数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;
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任务处理。所以如果实际业务场景静态分区能解决的,尽量使用静态分区即可。
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表的分区数量进行合理规划,避免过多的分区。
a. 尽量不要使用动态分区,因为动态分区的时候,将会为每一个分区分配reducer数量,当分区数量多的时候,reducer数量将会增加,对服务器是一种灾难。
b. 动态分区和静态分区的区别,静态分区不管有没有数据都会创建指定分区,动态分区是有结果集将创建,否则不创建。
c. hive动态分区的严格模式和hive严格模式是不同的。
hive提供的严格模式简述:
hive提供的严格模式,为了组织用户不小心提交恶意SQL
hive.mapred.mode=nostrict : strict
如果该模式值为strict,将会阻止一下三种查询:
a.对分区表查询,where条件中过滤字段没有分区字段;
b.笛卡尔积join查询,join查询语句中不带on条件或者where条件;
c.对order by查询,有order by的查询不太limit语句。
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');
上一篇:关于hive分区,你知道多少呢?
按例,我的个人公众号:鲁边社,欢迎关注

后台回复关键字 hive,随机赠送一本鲁边备注版珍藏大数据书籍。
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?
请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是