hive中的静态分区与动态分区

本文详细介绍了Hive中分区表的创建与使用方法,包括如何创建分区、向分区表插入数据以及动态分区的概念和实现方式。
hive中创建分区表没有什么复杂的分区类型(范围分区、列表分区、hash分区、混合分区等)。分区列也不是表中的一个实际的字段,而是一个或者多个伪列。意思是说在表的数据文件中实际上并不保存分区列的信息与数据。
下面的语句创建了一个简单的分区表:

create table partition_test
(member_id string,
name string
)
partitioned by (
stat_date string,
province string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';

这个例子中创建了stat_date和province两个字段作为分区列。通常情况下需要先预先创建好分区,然后才能使用该分区,例如:

alter table partition_test add partition (stat_date='20110728',province='zhejiang');

这样就创建好了一个分区。这时我们会看到hive在HDFS存储中创建了一个相应的文件夹:

$ hadoop fs -ls /user/hive/warehouse/partition_test/stat_date=20110728
Found 1 items
drwxr-xr-x - admin supergroup 0 2011-07-29 09:53 /user/hive/warehouse/partition_test/stat_date=20110728/province=zhejiang

每一个分区都会有一个独立的文件夹,下面是该分区所有的数据文件。在这个例子中stat_date是主层次,province是副层次,所有stat_date='20110728',而province不同的分区都会在/user/hive/warehouse/partition_test/stat_date=20110728 下面,而stat_date不同的分区都会在/user/hive/warehouse/partition_test/ 下面,如:

$ hadoop fs -ls /user/hive/warehouse/partition_test/
Found 2 items
drwxr-xr-x - admin supergroup 0 2011-07-28 19:46 /user/hive/warehouse/partition_test/stat_date=20110526
drwxr-xr-x - admin supergroup 0 2011-07-29 09:53 /user/hive/warehouse/partition_test/stat_date=20110728

注意,因为分区列的值要转化为文件夹的存储路径,所以如果分区列的值中包含特殊值,如 '%', ':', '/', '#',它将会被使用%加上2字节的ASCII码进行转义,如:

hive> alter table partition_test add partition (stat_date='2011/07/28',province='zhejiang');
OK
Time taken: 4.644 seconds

$hadoop fs -ls /user/hive/warehouse/partition_test/
Found 3 items
drwxr-xr-x - admin supergroup 0 2011-07-29 10:06 /user/hive/warehouse/partition_test/stat_date=2011% 2F07% 2F28
drwxr-xr-x - admin supergroup 0 2011-07-28 19:46 /user/hive/warehouse/partition_test/stat_date=20110526
drwxr-xr-x - admin supergroup 0 2011-07-29 09:53 /user/hive/warehouse/partition_test/stat_date=20110728

我使用一个辅助的非分区表partition_test_input准备向partition_test中插入数据:

hive> desc partition_test_input;
OK
stat_date string
member_id string
name string
province string

hive> select * from partition_test_input;
OK
20110526 1 liujiannan liaoning
20110526 2 wangchaoqun hubei
20110728 3 xuhongxing sichuan
20110728 4 zhudaoyong henan
20110728 5 zhouchengyu heilongjiang

然后我向partition_test的分区中插入数据:

hive> insert overwrite table partition_test partition(stat_date='20110728',province='henan') select member_id,name from partition_test_input where stat_date='20110728' and province='henan';
Total MapReduce jobs = 2
...
1 Rows loaded to partition_test
OK

还可以同时向多个分区插入数据,0.7版本以后不存在的分区会自动创建,0.6之前的版本官方文档上说必须要预先创建好分区:

hive>
> from partition_test_input
> insert overwrite table partition_test partition (stat_date='20110526',province='liaoning')
> select member_id,name where stat_date='20110526' and province='liaoning'
> insert overwrite table partition_test partition (stat_date='20110728',province='sichuan')
> select member_id,name where stat_date='20110728' and province='sichuan'
> insert overwrite table partition_test partition (stat_date='20110728',province='heilongjiang')
> select member_id,name where stat_date='20110728' and province='heilongjiang';
Total MapReduce jobs = 4
...
3 Rows loaded to partition_test
OK

特别要注意,在其他数据库中,一般向分区表中插入数据时系统会校验数据是否符合该分区,如果不符合会报错。而在hive中,向某个分区中插入什么样的数据完全是由人来控制的,因为分区键是伪列,不实际存储在文件中,如:


hive> insert overwrite table partition_test partition(stat_date='20110527',province='liaoning') select member_id,name from partition_test_input;
Total MapReduce jobs = 2
...
5 Rows loaded to partition_test
OK

hive> select * from partition_test where stat_date='20110527' and province='liaoning';
OK
1 liujiannan 20110527 liaoning
2 wangchaoqun 20110527 liaoning
3 xuhongxing 20110527 liaoning
4 zhudaoyong 20110527 liaoning
5 zhouchengyu 20110527 liaoning

可以看到在partition_test_input中的5条数据有着不同的stat_date和province,但是在插入到partition(stat_date='20110527',province='liaoning')这个分区后,5条数据的stat_date和province都变成相同的了,因为这两列的数据是根据文件夹的名字读取来的,而不是实际从数据文件中读取来的:

$ hadoop fs -cat /user/hive/warehouse/partition_test/stat_date=20110527/province=liaoning/000000_0
1,liujiannan
2,wangchaoqun
3,xuhongxing
4,zhudaoyong
5,zhouchengyu

下面介绍一下动态分区,因为按照上面的方法向分区表中插入数据,如果源数据量很大,那么针对一个分区就要写一个insert,非常麻烦。况且在之前的版本中,必须先手动创建好所有的分区后才能插入,这就更麻烦了,你必须先要知道源数据中都有什么样的数据才能创建分区。
使用动态分区可以很好的解决上述问题。动态分区可以根据查询得到的数据自动匹配到相应的分区中去。 
使用动态分区要先设置hive.exec.dynamic.partition参数值为true,默认值为false,即不允许使用:

hive> set hive.exec.dynamic.partition;
hive.exec.dynamic.partition=false
hive> set hive.exec.dynamic.partition=true;
hive> set hive.exec.dynamic.partition;
hive.exec.dynamic.partition=true

动态分区的使用方法很简单,假设我想向stat_date='20110728'这个分区下面插入数据,至于province插入到哪个子分区下面让数据库自己来判断,那可以这样写:

hive> insert overwrite table partition_test partition(stat_date='20110728',province)
> select member_id,name,province from partition_test_input where stat_date='20110728';
Total MapReduce jobs = 2
...
3 Rows loaded to partition_test
OK

stat_date叫做静态分区列,province叫做动态分区列。select子句中需要把动态分区列按照分区的顺序写出来,静态分区列不用写出来。这样stat_date='20110728'的所有数据,会根据province的不同分别插入到/user/hive/warehouse/partition_test/stat_date=20110728/下面的不同的子文件夹下,如果源数据对应的province子分区不存在,则会自动创建,非常方便,而且避免了人工控制插入数据与分区的映射关系存在的潜在风险。

注意,动态分区不允许主分区采用动态列而副分区采用静态列,这样将导致所有的主分区都要创建副分区静态列所定义的分区:

hive> insert overwrite table partition_test partition(stat_date,province='liaoning')
> select member_id,name,province from partition_test_input where province='liaoning';
FAILED: Error in semantic analysis: Line 1:48 Dynamic partition cannot be the parent of a static partition 'liaoning'

动态分区可以允许所有的分区列都是动态分区列,但是要首先设置一个参数hive.exec.dynamic.partition.mode :

hive> set hive.exec.dynamic.partition.mode;
hive.exec.dynamic.partition.mode=strict

它的默认值是strick,即不允许分区列全部是动态的,这是为了防止用户有可能原意是只在子分区内进行动态建分区,但是由于疏忽忘记为主分区列指定值了,这将导致一个dml语句在短时间内创建大量的新的分区(对应大量新的文件夹),对系统性能带来影响。
所以我们要设置:
hive> set hive.exec.dynamic.partition.mode=nostrick;

再介绍3个参数:
hive.exec.max.dynamic.partitions.pernode (缺省值100):每一个mapreduce job允许创建的分区的最大数量,如果超过了这个数量就会报错
hive.exec.max.dynamic.partitions (缺省值1000):一个dml语句允许创建的所有分区的最大数量
hive.exec.max.created.files (缺省值100000):所有的mapreduce job允许创建的文件的最大数量

当源表数据量很大时,单独一个mapreduce job中生成的数据在分区列上可能很分散,举个简单的例子,比如下面的表要用3个map:
1
1
1
2
2
2
3
3
3

如果数据这样分布,那每个mapreduce只需要创建1个分区就可以了: 
                 |1
map1 --> |1 
                 |1 

                 |2
map2 --> |2 
                 |2 

                 |3
map3 --> |3 
                 |3
但是如果数据按下面这样分布,那第一个mapreduce就要创建3个分区: 

                 |1
map1 --> |2 
                 |3 

                 |1
map2 --> |2 
                 |3 

                 |1
map3 --> |2 
                 |3

下面给出了一个报错的例子:
hive> set hive.exec.max.dynamic.partitions.pernode=4;
hive> insert overwrite table partition_test partition(stat_date,province)
> select member_id,name,stat_date,province from partition_test_input distribute by stat_date,province;
Total MapReduce jobs = 1
...
[Fatal Error] Operator FS_4 (id=4): Number of dynamic partitions exceeded hive.exec.max.dynamic.partitions.pernode.. Killing the job.
Ended Job = job_201107251641_0083 with errors
FAILED: Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.MapRedTask

为了让分区列的值相同的数据尽量在同一个mapreduce中,这样每一个mapreduce可以尽量少的产生新的文件夹,可以借助distribute by的功能,将分区列值相同的数据放到一起:

hive> insert overwrite table partition_test partition(stat_date,province)
> select member_id,name,stat_date,province from partition_test_input distribute by stat_date,province;
Total MapReduce jobs = 1
...
18 Rows loaded to partition_test
OK
<think> 我们正在讨论Hive中的动态分区静态分区。用户已经提供了三个引用,这些引用涉及动态分区静态分区的定义、设置以及使用场景。用户的问题明确要求解释两者的区别、特点及使用方法。 根据引用[1]和[3]: - 静态分区是手动指定分区值,而动态分区是根据数据的值自动分配分区。 - 静态分区创建表时或之后(使用ALTER)指定分区,而动态分区在插入数据时自动创建分区。 引用[2]提供了Hive设置动态分区的参数: - `hive.exec.dynamic.partition=true`:启用动态分区。 - `hive.exec.dynamic.partition.mode=nonstrict`:允许所有分区列都使用动态分区(非严格模式)。在严格模式下,必须至少有一个静态分区。 我们需要组织回答: 1. 定义和核心区别。 2. 特点(优缺点)。 3. 使用方法(包括SQL示例和配置)。 4. 使用场景。 同时,根据用户之前的提问历史,我们之前回答了Hive内部表和外部表的区别,所以可以适当关联(比如在分区表的使用上,内部表和外部表都可以使用分区,但分区管理方式相同)。 回答结构: 一、概念核心区别 二、静态分区详解(特点、操作步骤) 三、动态分区详解(特点、操作步骤、配置参数) 四、对比总结(优缺点、适用场景) 五、操作示例(静态分区插入、动态分区插入) 六、选择建议 最后,生成相关问题。 注意:在回答中,对于引用的地方,在段落末尾添加引用标识,例如[^1]。 </think> ### Hive 动态分区静态分区详解 以下从 **核心区别**、**特点对比**、**使用方法** 三个维度展开分析,结合 Hive SQL 示例说明: --- #### 一、核心区别 | **特性** | **静态分区** | **动态分区** | |------------------|---------------------------------------------|---------------------------------------------| | **分区指定方式** | 手动显式指定分区值 | 根据数据列值**自动推断**分区 | | **语法要求** | 必须硬编码分区值(如 `PARTITION (dt='2023-01')`) | 仅需指定分区列名(如 `PARTITION (dt)`) | | **灵活性** | 低(每次插入需修改 SQL) | 高(自动适应新分区值) | | **适用场景** | 分区值已知且固定 | 分区值动态变化或未知 | > **类比理解**: > - **静态分区** ≈ 图书馆管理员**手动**将书放入指定编号书架 > - **动态分区** ≈ 书籍根据条形码**自动滑入**对应分类区域 [^1] --- #### 二、特点对比 | **维度** | **静态分区** | **动态分区** | |----------------|-------------------------------------------|-------------------------------------------| | **性能** | ✅ 更快(直接定位分区) | ⚠️ 较慢(需扫描数据推断分区,资源消耗大) | | **易用性** | ⚠️ 需频繁修改 SQL | ✅ 一次配置自动处理 | | **数据准确性** | ✅ 高(人工控制) | ⚠️ 依赖源数据质量(列值错误导致分区错误) | | **并发控制** | ✅ 安全(单分区写入) | ⚠️ 需监控(可能同时创建多个分区) | --- #### 三、使用方法 ##### 1. **静态分区操作** ```sql -- 创建分区表 CREATE TABLE logs (msg STRING) PARTITIONED BY (dt STRING, hour STRING); -- 手动插入数据到指定分区(显式指定分区值) INSERT INTO TABLE logs PARTITION (dt='2023-10-01', hour='08') VALUES ('Error: connection timeout'); ``` ##### 2. **动态分区操作** ```sql -- 启用动态分区(必需配置) SET hive.exec.dynamic.partition = true; SET hive.exec.dynamic.partition.mode = nonstrict; -- 允许全动态分区[^2] -- 从源表动态导入(根据src表的dt/hour列自动创建分区) INSERT INTO TABLE logs PARTITION (dt, hour) SELECT msg, dt, hour FROM src_table; ``` > **关键配置**: > - `hive.exec.max.dynamic.partitions`:单次任务最大分区数(默认1000) > - `hive.exec.max.dynamic.partitions.pernode`:单节点最大分区数(默认100) ##### 3. **混合使用场景** ```sql -- 静态分区(年) + 动态分区(月、日) INSERT INTO sales PARTITION (year=2023, month, day) SELECT product, amount, month, day FROM raw_data; ``` --- #### 四、选择建议 | **场景** | **推荐方式** | **原因** | |-------------------------|--------------|------------------------------------------| | 分区值固定且少量 | 静态分区 | 性能高,操作简单 | | 分区值变化频繁/海量 | 动态分区 | 避免手动维护,适应新分区[^3] | | 增量数据导入(如每日) | 动态分区 | 自动创建当日分区 | | 历史数据修补 | 静态分区 | 精准控制目标分区 | > **动态分区典型用例**: > - 日志分析(按时间自动分区) > - 用户行为数据(按地区/设备自动分组)[^1] --- ### 相关问题 1. 动态分区导致小文件过多,如何优化 HDFS 存储? 2. 如何监控 Hive 动态分区的性能瓶颈? 3. 动态分区插入时出现数据倾斜该如何解决? 4. 静态分区动态分区Hive ACID 事务的支持有何差异
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值