分区表

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