ZhangZhihui's Blog  

 

load data local inpath '/data/dept.txt' into table dept_partitions partition(month='2020-01');

 

上面的Hive加载本地文件到分区表的语句执行完之后,在HDFS的文件系统中的'2020-01'目录下看到的是整个dept.txt文件还是只包含月份为2020-01的数据的文件?
 

你写的这个语句:

LOAD DATA LOCAL INPATH '/data/dept.txt' INTO TABLE dept_partitions PARTITION (month='2020-01');

在 Hive 里执行后,会发生这样的事情:

  1. LOAD DATA 不做数据过滤
    它不会去解析 dept.txt 的内容,也不会只把“月份=2020-01”的数据挑出来。
    Hive 只是把这个本地文件原封不动地拷贝(LOCAL 表示从本地文件系统复制)到分区对应的 HDFS 目录。

  2. 数据直接进入对应的分区目录
    Hive 会把 /data/dept.txt 文件复制到:

    /user/hive/warehouse/dept_partitions/month=2020-01/

    目录下(具体路径取决于你的 hive.metastore.warehouse.dir 配置)。
    文件名可能还是 dept.txt,也可能会改成 Hive 自动生成的名字(比如 000000_0),但内容就是你本地文件的全部内容。

  3. 文件内容不一定匹配分区值
    Hive 不会检查文件里的数据列是否真的都是 2020-01。分区值完全由你在 PARTITION (month='2020-01') 指定的参数决定,查询时 Hive 会用这个分区值来当做一列返回(即使文件里的数据和它不一致)。


结论
执行完这个 LOAD DATA LOCAL ... PARTITION ... 语句后,
HDFS month=2020-01 目录下放的就是整个 dept.txt 文件的全部数据,Hive 并不会只保留 month=2020-01 的行。

如果你想让分区目录里只包含符合分区条件的数据,就得先用 INSERT OVERWRITE ... SELECT ... WHERE month='2020-01' 这种方式去筛选。

 
You said:
执行完上面的语句后,我再执行如下语句: load data local inpath '/data/dept.txt' into table dept_partitions partition(month='2020-02'); Hive又会将整个dept.txt文件复制到2020-02目录下,对吗?
 
ChatGPT said:

对的 ✅。

你第二次执行

LOAD DATA LOCAL INPATH '/data/dept.txt' INTO TABLE dept_partitions PARTITION (month='2020-02');

Hive 会再次原封不动地把本地 /data/dept.txt 文件复制到分区目录:

.../dept_partitions/month=2020-02/

和第一次一样:

  • 不会检查文件里的 month 列值是否是 2020-02;

  • 不会做去重或过滤;

  • 只是把整个文件搬到新的分区目录。

所以最终 HDFS 上会出现两份一模一样的 dept.txt(或者 Hive 给它改的内部文件名):

 
.../dept_partitions/month=2020-01/000000_0
.../dept_partitions/month=2020-02/000000_0

内容完全相同,只是分区路径不同。


如果你想避免这种“数据内容和分区值可能不一致”的情况,就得先把文件导入临时表,再用 INSERT OVERWRITE ... SELECT ... WHERE ... 去按分区值写入。

 

You said:
如果这样的话,Hive岂不是会在HDFS文件系统中生成很多重复的文件,这样的设计岂不是很不合理?
 
ChatGPT said:

是的,从存储利用率的角度看,这确实可能造成大量重复文件,看起来有点“不合理”,但 Hive 这么设计是有历史和技术背景的。

我给你拆开解释一下:


1. Hive 的 LOAD DATA 设计初衷

  • LOAD DATA 在 Hive 中本质上只是一个 文件搬运操作movecopy),不是数据导入+转换。

  • 它的目标是快速让数据可查询,而不是保证数据内容和分区字段一致。

  • 因为它最早就是给“用户自己已经准备好分区目录+数据”的场景用的。
    例如:

    /warehouse/sales/year=2024/month=01/ <--- 已经是1月的数据
    /warehouse/sales/year=2024/month=02/ <--- 已经是2月的数据

    Hive 只负责登记元数据,不去动内容。


2. 为什么会造成重复文件

  • 你如果多次 LOAD DATA 同一个原始文件到不同分区目录,Hive 并不会检查文件内容,只会简单地复制或移动

  • HDFS 不像数据库那样有“行存储”和“索引指针”,它没有“去重存储”的机制(除非额外用 HDFS 层的硬链接或 dedup 工具)。

  • 结果就是不同分区目录下可能会放同一份内容的多个拷贝。


3. 为什么 Hive 没做自动过滤

  • 过滤意味着 Hive 必须解析文件,读取每一行,然后判断分区列是否匹配,这个过程就是一次 MapReduceTez/Spark 任务。

  • 这样性能会比 LOAD DATA 慢很多,失去了快速导入的优势。

  • 因此 Hive 设计了另一种方式:INSERT OVERWRITE ... SELECT ... WHERE 来做筛选,而不是在 LOAD DATA 里搞。


4. 合理的做法

如果你不想在 HDFS 产生大量重复文件,可以用下面的流程:

  1. 把数据用 LOAD DATA 或 HDFS put 放到一个“原始数据表”里(非分区)。

  2. INSERT OVERWRITE TABLE ... PARTITION(...) SELECT ... 过滤并写入目标分区表。

这样每条数据只存一份,分区目录里的内容和分区值保持一致。


总结

  • Hive 的 LOAD DATA 更像是“注册文件到表”而不是“智能导入”。

  • 直接多次 LOAD 同一个文件到不同分区,HDFS 就会存多份。

  • 如果追求存储合理性,要自己先过滤再写分区。

 

我可以帮你画一张 Hive 数据加载三种模式对比图(LOAD / INSERT / 外部分区),这样你以后就一眼能看出什么情况下会重复存储,什么情况下不会。
你要我画吗?

 
You said:
好的。谢谢。
 
ChatGPT said:

好,那我帮你画一个 Hive 数据加载三种模式对比图,让你清楚看到什么时候会产生重复文件,什么时候不会。


Hive 数据加载模式对比

加载方式过程是否产生重复文件场景适用
① LOAD DATA LOCAL/INPATH 直接把文件 移动/复制 到分区或表目录,不解析内容 ✅ 可能(多次LOAD同一文件到不同分区就会多份) 已经提前按分区准备好数据的场景
② INSERT OVERWRITE/INTO 读取数据 → 运行SQL过滤/转换 → 写入目标分区文件 ❌ 不会重复(只保留符合条件的数据) 数据需要按分区字段筛选、去重、转换的场景
③ 外部分区表 (External Table) + ALTER ADD PARTITION 数据文件已在HDFS固定目录,Hive只登记元数据,不复制文件 ❌ 不会重复(文件只存一份) 已经有HDFS现成分区目录的场景

流程图

        (本地/HDFS原始文件)
                 │
        ┌────────┼──────────┐
        │        │          │
   LOAD DATA   INSERT   外部分区表+ADD PARTITION
     │          │           │
 [文件直接搬]  [解析/过滤]  [只登记元数据]
     │          │           │
 文件直接出现在  分区目录  目录已存在
  分区目录下     仅符合条件的数据
     │          │
 可能有重复      无重复

 


存储策略建议

  • 不想重复存储 → 先放到“原始表”里,再 INSERT OVERWRITE 按分区字段写入目标表。

  • 要快速加载 → 直接 LOAD DATA,但前提是你确信文件本身已按分区切好。

  • 已经有分区目录 → 建立外部分区表,ALTER TABLE ... ADD PARTITION,不搬文件。

 

🛠 Hive 分区加载最佳实践流程

                【数据来源】
            ┌───────────────┐
            │  本地/外部系统 │
            │   dept.txt    │
            └───────┬───────┘
                    │
                    ▼
          【HDFS 原始数据区】
   /data/raw/dept/dept.txt  (非分区原始表)
                    │
                    ▼
     ┌───────────────────────────┐
     │   Step 1: 建立临时原始表   │
     │   CREATE TABLE dept_raw    │
     │   (...字段...)             │
     │   ROW FORMAT ...           │
     │   STORED AS ...            │
     │   LOCATION '/data/raw/dept'│
     └───────────┬───────────────┘
                 │  (LOAD DATA一次即可)
                 ▼
     ┌───────────────────────────┐
     │ Step 2: 目标分区表         │
     │ CREATE TABLE dept_partitions│
     │ (...字段...)                │
     │ PARTITIONED BY (month STRING)│
     │ STORED AS ...               │
     └───────────┬────────────────┘
                 │
                 ▼
     ┌───────────────────────────┐
     │ Step 3: 按分区字段写入     │
     │ INSERT OVERWRITE TABLE     │
     │ dept_partitions            │
     │ PARTITION(month)           │
     │ SELECT col1, col2, ...,    │
     │        month               │
     │ FROM dept_raw;             │
     └───────────────────────────┘

 


📌 核心原则

  1. 一次 LOAD,多次分区写入

    • 所有原始文件只加载一次到非分区表(避免多份重复文件)。

  2. 分区值由 SQL 决定

    • SELECT 中的字段值自动生成分区,保证分区字段和数据一致。

  3. 按需更新分区

    • 新数据来时,只 INSERT OVERWRITE 目标分区,不影响其他分区。


🎯 优势

  • 无重复文件:HDFS 中同一份数据只存一份。

  • 分区字段和数据强一致:不会出现 month=2020-01 目录里有 2020-02 的行。

  • 可维护性强:分区更新可独立执行,不影响全表。

 

那总结一下今天的结论:

  • LOAD DATA 在 Hive 里只是搬文件,不会过滤数据,容易造成同一份文件多份存储。

  • 避免重复 最稳妥的做法是:先放到非分区原始表,再用 INSERT OVERWRITE 按分区字段写入目标表。

  • 外部分区表 适合已有分区目录的场景,Hive 只登记元数据,不会多存文件。

这样你以后在 Hive 里加载分区数据时,就能既节省 HDFS 空间,又保持数据和分区一致了。

 

posted on 2025-08-12 09:52  ZhangZhihuiAAA  阅读(35)  评论(0)    收藏  举报