分区表
Hive 分区表:把数据按文件夹拆开查,别再全表扫描了
Hive 表里的数据量一大(比如每天几百万条日志),全表扫描就扛不住了——扫一次几分钟甚至十几分钟。
分区表就是干这个的:把数据按某个字段(比如日期)拆成不同文件夹,查询时只扫对应的文件夹,不扫全表。
普通表:
/warehouse/order_table/
├── 所有数据混在一起(扫全表)
分区表:
/warehouse/order_table/
├── date=2026-07-01/
│ └── 这一天所有数据
├── date=2026-07-02/
│ └── 这一天所有数据
└── date=2026-07-03/
└── 这一天所有数据
查询 WHERE date='2026-07-02' 时,只扫 date=2026-07-02 这个文件夹,其他文件夹不碰。
创建分区表
在普通建表语句上加 PARTITIONED BY 就行了。
CREATE TABLE order_partitioned (
id INT,
amount DOUBLE
)
PARTITIONED BY (dt STRING COMMENT '日期,格式 yyyy-MM-dd')
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
注意: 分区列 dt 不在表的普通列里,它是独立的”目录层”字段。插入数据时单独指定。
插入数据
方式1:INSERT 语句
-- 往 2026-07-01 这个分区插数据
INSERT INTO order_partitioned PARTITION (dt='2026-07-01')
VALUES (1, 100.0), (2, 200.0);
-- 往 2026-07-02 这个分区插数据
INSERT INTO order_partitioned PARTITION (dt='2026-07-02')
VALUES (3, 150.0);
方式2:LOAD DATA
-- 把文件导入到 2026-07-01 分区
LOAD DATA LOCAL INPATH '/tmp/orders.txt'
INTO TABLE order_partitioned
PARTITION (dt='2026-07-01');
文件内容只需要 id, amount 两列,dt 由分区目录指定。
查询数据——必须带分区条件
-- 正确:只扫 dt=2026-07-01 这个文件夹
SELECT * FROM order_partitioned WHERE dt='2026-07-01';
-- 错误:不带分区条件,全表扫描
SELECT * FROM order_partitioned WHERE amount > 100;
分区表不带分区条件 = 全表扫描 = 白建分区了。
查看和管理分区
查看有哪些分区:
SHOW PARTITIONS order_partitioned;
-- dt=2026-07-01
-- dt=2026-07-02
手动添加分区(数据已经传上去了,但元数据没有):
ALTER TABLE order_partitioned ADD PARTITION (dt='2026-07-03');
删除分区(同时删除元数据和数据):
ALTER TABLE order_partitioned DROP PARTITION (dt='2026-07-01');
修复元数据(扫描 HDFS 目录,自动添加新增的分区):
MSCK REPAIR TABLE order_partitioned;
二级分区:一级不够再分一层
如果每天的数据量还是很大(比如一天几百万条),可以在日期下面再分一层——比如按小时:
/warehouse/order_partitioned_2/
├── dt=2026-07-01/
│ ├── hour=00/
│ ├── hour=01/
│ └── ...
└── dt=2026-07-02/
├── hour=00/
└── hour=01/
建表:
CREATE TABLE order_partitioned_2 (
id INT,
amount DOUBLE
)
PARTITIONED BY (
dt STRING COMMENT '日期',
hour STRING COMMENT '小时'
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
插入:
INSERT INTO order_partitioned_2 PARTITION (dt='2026-07-01', hour='10')
VALUES (1, 100.0);
查询:
-- 只查某一天
SELECT * FROM order_partitioned_2 WHERE dt='2026-07-01';
-- 只查某一天某个小时(最精确)
SELECT * FROM order_partitioned_2 WHERE dt='2026-07-01' AND hour='10';
二级分区比一级分区更精细,但分区数量也会成倍增加,需要权衡。
分区策略设计:选什么字段、分多细?
分区列选什么字段?
选查询时最常用的 WHERE 条件字段。
| 推荐 | 不推荐 |
|---|---|
| 日期(dt) | 用户 ID(基数太大,分区过多) |
| 地域(region) | 金额(查询时不会按金额分区过滤) |
| 业务类型(type) | 时间戳(每行一个值,分区数 = 行数) |
分区粒度怎么定?
| 数据量 | 推荐粒度 | 分区数(1年) |
|---|---|---|
| 每天 < 1GB | 按天 | 365 个 |
| 每天 1-100GB | 按天 | 365 个 |
| 每天 > 100GB | 按天 + 按小时(二级分区) | 365 × 24 = 8760 个 |
| 每月 < 1GB | 按月 | 12 个 |
分区太少 → 查询时还是扫很多数据,分区没意义。
分区太多 → 小文件多,元数据压力大(Hive Metastore 扛不住)。
一般建议:每天数据量在 1GB-100GB 之间,按天分区就够。超过 100GB 再考虑二级分区。
常见问题
1. 分区表查询很慢,明明有分区条件?
检查执行计划,看是不是真的走了分区过滤:
EXPLAIN SELECT * FROM order_partitioned WHERE dt='2026-07-01';
如果执行计划里出现 Scan Partition 且只扫了 dt=2026-07-01,说明走对了。如果出现 Scan Full Table,说明分区条件没生效(可能是字段名写错了或者 dt 不是分区列)。
2. 加了新分区,SHOW PARTITIONS 看不到?
数据是通过 HDFS 直接上传的,元数据没同步。执行:
MSCK REPAIR TABLE order_partitioned;
3. 分区太多,SHOW PARTITIONS 很久?
分区数超过几千的时候,SHOW PARTITIONS 会慢。考虑合并分区粒度(比如从按小时改成按天)。
4. 删除分区后数据还在 HDFS 上?
DROP PARTITION 会同时删元数据和数据。如果只是想删元数据保留数据,先 hdfs dfs -mv 把数据移走,再 DROP PARTITION。