Hive极简入门-用SQL玩转大数据仓库
Hive 极简入门 — 用 SQL 玩转大数据仓库,看完这篇就够了
写 MapReduce 太累了。从一个需求到产出,需要编写 Mapper、Reducer、Runner 三个类,编译打包提交。如果每个统计分析都这么搞,数据分析师可以直接辞职了。Hive 的出现,让这一切变得简单——你只需要写 SQL,Hive 帮你翻译成 MapReduce。
一、开篇:从"为什么要用 SQL 查大数据"说起
1.1 数据仓库的诞生
在 Hive 出现之前(大约 2010 年前),Hadoop 的使用方式是:
写 Java MapReduce 程序 → 编译 Jar → 提交到集群 → 等待结果 → 查看输出
完成一个简单的"统计日活跃用户"的需求,一个熟练的 Java 工程师可能需要半天到一天。
Facebook 每天有海量的日志需要分析,他们发现一个问题:公司里会 SQL 的人远远多于会 MapReduce 的人。
于是 Facebook 开发了 Hive——一个能将 SQL 语句自动翻译成 MapReduce 任务 的工具。
1.2 Hive 本质上是什么?
Hive 是基于 Hadoop 的数据仓库工具,它可以将结构化的数据文件映射为一张数据库表,将 SQL 语句转换为 MapReduce 任务来执行。
- 不存储数据:数据还是在 HDFS 上
- 不执行计算:计算还是 MapReduce(后来也支持 Tez、Spark)
- 只做一件事:把 SQL 翻译成 MapReduce 任务
二、数据仓库基础概念
2.1 什么是数据仓库?
数据仓库(Data Warehouse)是一个面向主题的、集成的、随时间变化的、相对稳定的数据集合,用于支持企业或组织的决策分析处理。
拆解这个定义中的四个关键词:
| 关键词 | 含义 | 举例 |
|---|---|---|
| 面向主题 | 按业务主题组织数据,而非按应用流程 | 订单主题、用户主题、商品主题 |
| 集成的 | 数据来自多个系统,经过清洗、统一格式 | 把不同系统的同一字段名统一、度量单位统一 |
| 随时间变化 | 保存历史数据,支持时间维度的分析 | 可以查去年同期的销售额 |
| 相对稳定 | 数据主要是批量导入和查询,不频繁修改 | 写入后很少改,主要是读 |
2.2 数据仓库 vs 传统数据库
| 对比维度 | 数据仓库(OLAP) | 传统数据库(OLTP) |
|---|---|---|
| 主要操作 | 查询分析(读多写少) | 增删改查(读写均衡) |
| 数据量 | TB ~ PB 级 | GB ~ TB 级 |
| 响应时间 | 秒 ~ 分钟 | 毫秒级 |
| 设计目标 | 分析决策 | 事务处理 |
| 历史数据 | 保留多年历史 | 通常只保留近期 |
| 数据模型 | 星型/雪花模型(多维) | 实体关系模型(3NF) |
2.3 数据仓库的四层架构
数据仓库四层架构
数据源层
├── 操作数据库(MySQL/Oracle 等业务库)
├── 文档资料(Excel、CSV)
└── 外部信息源(第三方 API、日志文件)
│
▼ ETL 过程
数据存储及管理层
├── 抽取(Extract)— 从各数据源获取
├── 转换(Transform)— 清洗、去重、格式化
└── 装载(Load)— 加载到数据仓库
│
▼
OLAP 服务器
└── 按多维数据模型重组 → 支持多角度、多层次分析
│
▼
前端工具
├── 数据查询(SQL 查询)
├── 数据报表(可视化图表)
├── 数据分析(即席分析)
└── 数据挖掘(预测建模)
2.4 数据模型:星型 vs 雪花
星型模型(Star Schema)
┌──────────────┐
│ 时间维度表 │
└──────┬───────┘
│
┌──────────────┐ ┌──────┴───────┐ ┌──────────────┐
│ 用户维度表 │◄───────│ 事实表 │───────►│ 商品维度表 │
│ │ │ (销售额等) │ │ │
└──────────────┘ └──────┬───────┘ └──────────────┘
│
┌──────┴───────┐
│ 地区维度表 │
└──────────────┘
- 特点:中间一张事实表(Fact Table),周围放射状连接多个维度表(Dimension Table)
- 优点:查询性能好(一次 JOIN 即可关联所有维度)——适合查询优先场景
- 缺点:数据冗余(维度表没有进一步规范化)
雪花模型(Snowflake Schema)
┌──────────────┐
│ 时间维度表 │
└──────┬───────┘
│
┌──────────────┐ ┌──────┴───────┐ ┌──────────────┐
│ 用户维度表 │◄───────│ 事实表 │───────►│ 商品维度表 │
│ │ │ └──────────────┘ │ │ │
│ ▼ │ │ ▼ │
│ 用户分类表 │ │ 商品分类表 │
└──────────────┘ └──────────────┘
- 特点:维度表进一步分解为子维度表(规范化到 3NF),形状像多个雪花连接
- 优点:减少数据冗余,节省存储空间
- 缺点:查询时需 JOIN 更多表,性能略低
简单记忆:星型——“一次 JOIN,全部关联”;雪花——“多 JOIN 几次,但更省空间”。
三、Hive 核心概念
3.1 Hive 体系架构
Hive 系统架构
┌─────────────────────────────────────────────────────┐
│ 用户接口 │
│ CLI(命令行) | WebUI(Web 界面) | HiveServer2 │
└──────────────────────┬──────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────┐
│ Thrift 跨语言服务 │
│ (支持 Java/Python/PHP 等语言连接 Hive) │
└──────────────────────┬──────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────┐
│ 底层驱动引擎(Driver) │
│ │
│ SQL → SQL Parser → Compiler → Optimizer → Executor │
│ ↓ ↓ │
│ AST(语法树) 执行计划(DAG) │
└──────────────────────┬──────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────┐
│ Metastore(元数据存储) │
│ ┌────────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ 表的结构 │ │ 分区信息 │ │ HDFS 路径映射 │ │
│ └────────────┘ └──────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────┘
四大组件说明:
| 组件 | 功能 | 说明 |
|---|---|---|
| 用户接口 | 让用户输入 SQL | CLI(命令行)、WebUI、HiveServer2(JDBC/ODBC) |
| Thrift 服务 | 跨语言通信 | 允许不同编程语言的客户端访问 Hive |
| 底层驱动引擎 | SQL → MapReduce | 解析 SQL → 编译为执行计划 → 优化 → 执行 |
| Metastore | 元数据存储 | 存表名、字段名、字段类型、分区信息、HDFS 路径等 |
3.2 Hive vs MySQL — 根本差异
初学者最容易把 Hive 当成"大号 MySQL",但两者的设计哲学完全不同:
| 对比项 | Hive | MySQL |
|---|---|---|
| 查询语言 | HQL(类 SQL) | SQL |
| 数据存储位置 | HDFS(分布式文件系统) | 本地磁盘(块设备) |
| 数据格式 | 用户定义(TEXTFILE/ORC/Parquet) | 系统决定(InnoDB 行格式等) |
| 数据更新 | ❌ 不支持(HDFS 只读追加) | ✅ UPDATE/DELETE |
| 事务 | ❌ 不支持(Hive 3.0 后部分支持 ACID) | ✅ 完整 ACID |
| 执行延迟 | ⏳ 高(秒~分钟级,每次查询转 MapReduce) | ⚡ 低(毫秒级) |
| 可扩展性 | 高(线性扩展,上千节点) | 低(单机瓶颈) |
| 数据规模 | 大(PB 级) | 小(TB 级以下) |
深入理解:为什么 Hive 不支持事务和更新?
Hive 的数据存储在 HDFS 上,而 HDFS 的设计是"一次写入,多次读取"(Write Once, Read Many)。HDFS 不能随机修改文件的某个字节——只能追加或覆盖整个文件。所以 Hive 也不支持传统的行级更新和事务。
3.3 Hive 安装模式
| 模式 | 元数据存储 | 特点 | 适用场景 |
|---|---|---|---|
| 嵌入模式 | 内嵌 Derby 数据库 | 配置最简单,但一次只能连接一个客户端 | 个人测试 |
| 本地模式 | 外部数据库(如 MySQL) | 元数据服务与 Hive 同进程,无需单独启动 Metastore 服务 | 开发环境 |
| 远程模式 | 外部数据库(如 MySQL) | 单独启动 Metastore 服务,多客户端可同时远程连接 | 生产环境 |
企业常用远程模式:因为生产环境中,用户通常不能直接访问 Hive 部署的服务器,必须通过远程 Metastore 服务进行连接。
四、数据类型
4.1 基础数据类型
| 类型 | 描述 | 取值/精度 |
|---|---|---|
| TINYINT | 1 字节整数 | -128 ~ 127 |
| SMALLINT | 2 字节整数 | -32,768 ~ 32,767 |
| INT | 4 字节整数 | -2³¹ ~ 2³¹-1 |
| BIGINT | 8 字节整数 | -2⁶³ ~ 2⁶³-1 |
| FLOAT | 4 字节单精度浮点数 | — |
| DOUBLE | 8 字节双精度浮点数 | — |
| DECIMAL | 任意精度的小数 | DECIMAL(10,2) 共 10 位,小数 2 位 |
| STRING | 变长字符串 | — |
| VARCHAR(n) | 变长,有长度限制 | — |
| CHAR(n) | 定长字符串 | — |
| TIMESTAMP | 时间戳(纳秒精度) | 2026-06-18 12:30:00 |
| DATE | 日期 | 2026-06-18 |
| BOOLEAN | 布尔值 | TRUE / FALSE |
4.2 复杂数据类型
| 类型 | 说明 | 举例 |
|---|---|---|
| ARRAY | 有序数组,元素类型相同 | ARRAY(1, 2, 3) |
| MAP<key, value> | 无序键值对 | MAP('name', 'Tom', 'age', '20') |
| STRUCTname:type,... | 命名结构体,字段可不同类型 | STRUCT('Tom', 20) |
建表示例(含复杂类型):
CREATE TABLE student(
id INT,
name STRING,
scores ARRAY<INT>, -- 多门成绩
address STRUCT<city:STRING, district:STRING>, -- 地址结构化
subject_score MAP<STRING, INT> -- 科目→成绩
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
COLLECTION ITEMS TERMINATED BY '_'
MAP KEYS TERMINATED BY ':';
五、数据库与表操作(DDL 详解)
5.1 数据库操作
-- 创建数据库
CREATE DATABASE IF NOT EXISTS my_db
COMMENT '我的数据库'
LOCATION '/user/hive/my_db';
-- 查看所有数据库
SHOW DATABASES;
-- 查看数据库详情
DESC DATABASE my_db;
-- 切换数据库
USE my_db;
-- 修改数据库属性
ALTER DATABASE my_db SET DBPROPERTIES ('owner' = 'admin');
-- 删除数据库(CASCADE 可删除非空数据库)
DROP DATABASE IF EXISTS my_db CASCADE;
5.2 创建数据表(完整语法)
CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] table_name
[(col_name data_type [COMMENT col_comment], ...)]
[COMMENT table_comment]
[PARTITIONED BY (col_name data_type, ...)]
[CLUSTERED BY (col_name, ...) [SORTED BY (col_name [ASC|DESC], ...)] INTO n BUCKETS]
[ROW FORMAT row_format]
[STORED AS file_format]
[LOCATION hdfs_path]
[TBLPROPERTIES (property_name=property_value, ...)];
各个子句详解:
| 子句 | 用途 | 示例 |
|---|---|---|
| TEMPORARY | 临时表(仅当前会话可见) | CREATE TEMPORARY TABLE ... |
| EXTERNAL | 外部表(数据生命周期不受 Hive 管控) | CREATE EXTERNAL TABLE ... |
| IF NOT EXISTS | 表已存在时不报错 | 推荐总是加上 |
| COMMENT | 注释 | COMMENT '用户信息表' |
| PARTITIONED BY | 分区 | PARTITIONED BY (city STRING) |
| CLUSTERED BY | 分桶 | CLUSTERED BY (user_id) INTO 4 BUCKETS |
| SORTED BY | 桶内排序 | SORTED BY (name ASC) |
| ROW FORMAT | 行格式(字段/集合/Map 分隔符) | ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' |
| STORED AS | 存储格式 | STORED AS TEXTFILE / ORC / PARQUET |
| LOCATION | 数据在 HDFS 上的位置(仅外部表常用) | LOCATION '/user/hive/warehouse/person' |
六、内部表 vs 外部表(重点展开)
6.1 两者的本质区别
| 对比维度 | 内部表(Managed Table) | 外部表(External Table) |
|---|---|---|
| 数据目录 | Hive 仓库目录 | 自定义 LOCATION 目录 |
| 数据生命周期 | Hive 控制——删表即删数据 | Hive 不管数据——删表只删元数据 |
| 谁负责创建数据 | Hive 自己(通过 LOAD DATA 或 INSERT) | 外部系统(日志系统、ETL 工具等事先放好) |
| 适用场景 | 清洗加工后的中间结果表 | 直接映射外部已有数据 |
一句话总结:
内部表:Hive 对你负责到底(管理数据生命周期,删表删数据);外部表:Hive 只看看不碰(只记录元数据,删表不删数据)。
6.2 LOCATION 的底层机制
CREATE EXTERNAL TABLE person (
id INT,
name STRING,
age INT
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE
LOCATION '/hivedb/person'; -- ← 指向 HDFS 上的一个目录
关键点:LOCATION 必须指向一个目录,不能指向文件!
✅ 正确:LOCATION '/hivedb/person'
Hive 会扫描这个目录下的所有文件
❌ 不要:LOCATION '/hivedb/person.txt'
Hive 会把 person.txt 当目录处理,读不到数据
为什么是目录不是文件?
一张表的完整数据通常分布在多个文件中。如果 LOCATION 只能是单个文件,每次新增数据都要改表定义。指向目录,意味着该目录下的所有文件都是这张表的数据。
一表一目录的最佳实践:
/hivedb/
├── person/ ← 表 person 的数据目录
│ ├── part-00000
│ └── part-00001
├── order/ ← 表 order 的数据目录
│ └── part-00000
└── product/ ← 表 product 的数据目录
└── part-00000
这样写 LOCATION 就可以精确指向每张表的专属目录,互不干扰。
6.3 外部表还需要 LOAD DATA 吗?
分两种情况:
| 场景 | 操作 | 说明 |
|---|---|---|
| 数据已经在 HDFS 上 | 只需 CREATE EXTERNAL TABLE ... LOCATION '/path' | 创建表的一瞬间数据"加载"完成 |
| 后续有新数据需要导入 | 也可以使用 LOAD DATA INPATH '...' INTO TABLE external_table | LOAD DATA 底层只是把文件移到 LOCATION 目录下 |
-- 场景 A:数据已在 HDFS
CREATE EXTERNAL TABLE log_20260618 (...) LOCATION '/logs/2026/06/18';
-- 表创建完,直接可以 SELECT * FROM log_20260618
-- 场景 B:后续追加新文件
LOAD DATA INPATH '/tmp/new_log.txt' INTO TABLE log_20260618;
-- 把 new_log.txt 移到 /logs/2026/06/18/ 目录下
七、分区表(Partition)— 按目录组织数据
7.1 为什么需要分区?
想象一张全国用户表,有 10 亿条数据。每次查询都全表扫描一次?太慢了。如果按"省份"分区,查询
where province='北京'就只需扫描北京的文件。
分区的本质:在 HDFS 上表现为目录层次。
未分区的表:
/user/hive/warehouse/person/
├── data1.txt
├── data2.txt
└── data3.txt
(查询时扫描所有文件)
按省份分区的表:
/user/hive/warehouse/person/
├── province=beijing/
│ └── data.txt
├── province=shanghai/
│ └── data.txt
└── province=guangdong/
└── data.txt
(查询 where province='beijing' 时,只扫描 beijing 目录!)
7.2 静态分区
-- 创建分区表
CREATE TABLE t_user_p (
id INT,
name STRING
)
PARTITIONED BY (country STRING) -- ★ 分区字段(不是实际数据中的字段)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
-- 加载数据到指定分区(静态分区)
LOAD DATA LOCAL INPATH '/hivedata/user_p.txt'
INTO TABLE t_user_p
PARTITION (country='USA'); -- 手动指定分区值
-- 新增分区
ALTER TABLE t_user_p ADD PARTITION (country='China')
LOCATION '/user/hive/warehouse/itcast.db/t_user_p/country=China';
-- 修改分区名
ALTER TABLE t_user_p PARTITION (country='China')
RENAME TO PARTITION (country='Japan');
-- 删除分区
ALTER TABLE t_user_p DROP IF EXISTS PARTITION (country='Japan');
7.3 动态分区
当数据量很大(比如每天几百万条),手动写分区太累了。Hive 提供了动态分区——自动根据字段值创建分区。
-- 开启动态分区
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict; -- nonstrict 允许所有分区都是动态的
-- 动态分区插入
INSERT OVERWRITE TABLE t_user_p
PARTITION (country) -- 只写字段名,不写具体值
SELECT id, name, country -- 最后一个字段作为分区字段
FROM source_table;
⚠️ 注意:
SELECT语句中的最后一个字段必须是分区字段。Hive 会根据这个字段的值自动决定数据写入哪个分区目录。
八、桶表(Bucket)— 按文件组织数据
8.1 为什么需要分桶?
分区是在目录级别做切分,分桶是在文件级别做切分。
分区 = 粗粒度(按目录分),分桶 = 细粒度(按文件分)
不分桶的表(一个目录下可能有很多大文件):
/user/hive/warehouse/person/
├── large_file_1.txt
├── large_file_2.txt
└── large_file_3.txt
分桶的表(Hash 均匀分布到多个文件中):
/user/hive/warehouse/person/
├── 000000_0 ← Hash(user_id) % 4 = 0 的数据
├── 000001_0 ← Hash(user_id) % 4 = 1 的数据
├── 000002_0 ← Hash(user_id) % 4 = 2 的数据
└── 000003_0 ← Hash(user_id) % 4 = 3 的数据
8.2 分桶的原理
CREATE TABLE stu_buck (
Sno INT, Sname STRING, Sex STRING, Sage INT, Sdept STRING
)
CLUSTERED BY (Sno) INTO 4 BUCKETS -- 按 Sno 分 4 个桶
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
数据路由规则:Hash(Sno) % 4 → 决定每条数据进入哪个桶文件。
8.3 ⚠️ 分桶表加载数据的特殊之处
绝对不能直接使用 LOAD DATA 加载分桶表!
-- ❌ 错误方式:LOAD DATA 只是文件搬运,不做 Hash
LOAD DATA LOCAL INPATH '/hivedata/student.txt' INTO TABLE stu_buck;
原因:
LOAD DATA底层只是 HDFS 的mv操作——把源文件移动到目标目录。它不会逐行读取、计算 Hash、分配到不同文件。结果就是:所有数据挤在一个文件中,分桶规则彻底失效。
正确方式(两步走):
-- Step 1:先用 LOAD DATA 加载到临时表
CREATE TABLE student_tmp (
Sno INT, Sname STRING, Sex STRING, Sage INT, Sdept STRING
) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
LOAD DATA LOCAL INPATH '/hivedata/student.txt' INTO TABLE student_tmp;
-- Step 2:开启分桶,通过 INSERT ... SELECT 触发 MapReduce 计算 Hash
set hive.enforce.bucketing = true;
INSERT OVERWRITE TABLE stu_buck
SELECT * FROM student_tmp
CLUSTER BY (Sno); -- ★ CLUSTER BY = DISTRIBUTE BY + SORT BY
INSERT ... SELECT会触发 MapReduce 任务,在 Reduce 端对 Sno 计算 Hash 并按结果分发到 4 个桶文件中。
8.4 SORTED BY — 桶内排序有什么用?
CLUSTERED BY (Sno) SORTED BY (Sname ASC) INTO 4 BUCKETS
每个桶内的数据不仅按 Sno 分桶,还按 Sname 排序存储。这有什么用?
SMB Join(Sort Merge Bucket Join):如果两张表按相同的键分桶,并且桶内按相同键排序,JOIN 时不需要全表 Shuffle——只需顺序读取两个桶文件,用双指针归并匹配即可。性能提升巨大!
九、数据操作(DML 详解)
9.1 SELECT 完整语法
SELECT [ALL | DISTINCT] select_expr, ...
FROM table_reference
[JOIN table_other ON expr]
[WHERE where_condition]
[GROUP BY col_list]
[HAVING having_condition]
[CLUSTER BY col_list | [DISTRIBUTE BY col_list] [SORT BY col_list]]
[ORDER BY col_list]
[LIMIT N];
9.2 INSERT INTO vs INSERT OVERWRITE
| 操作 | HDFS 底层行为 | 效果 |
|---|---|---|
| INSERT INTO | 追加新文件到目录 | 保留原有数据,新增数据 |
| INSERT OVERWRITE | 先删旧文件,再写新文件 | 覆盖原有数据 |
-- INTO:追加
INSERT INTO TABLE person SELECT * FROM student;
-- OVERWRITE:覆盖(目标表原有数据会被清空)
INSERT OVERWRITE TABLE person SELECT * FROM student;
导出到目录时只能用 OVERWRITE:
-- 导出到本地目录 INSERT OVERWRITE LOCAL DIRECTORY '/tmp/export/' SELECT * FROM person; -- 导出到 HDFS 目录 INSERT OVERWRITE DIRECTORY '/hivedb/export/' SELECT * FROM person;
9.3 四大排序语义(重点对比)
这是 Hive 初学者最容易混淆的概念。我们用一个求 Top N 的实际案例来理解。
CASE:找出年龄最大的 5 个人
-- ❌ 错误写法
set mapred.reduce.tasks=2;
SELECT * FROM person SORT BY age DESC LIMIT 5;
为什么错?
SORT BY 是局部排序——每个 Reducer 内部排序,但不同 Reducer 之间不排序。
Reducer 0(分到年龄较小的 50%): 年龄排前 5 → 60, 55, 50, 48, 45
Reducer 1(分到年龄较大的 50%): 年龄排前 5 → 80, 75, 70, 65, 62
最终输出 10 条(每个 Reducer 各 5 条):
60, 55, 50, 48, 45, 80, 75, 70, 65, 62
↑ 这根本不是全局前 5!
✅ 正确写法
-- ORDER BY 强制全局排序(自动设为 1 个 Reducer)
SELECT * FROM person ORDER BY age DESC LIMIT 5;
ORDER BY 保证全局有序,但有一个 Reducer 的性能瓶颈。
🚀 企业级优化(两阶段聚合)
当数据量极大时,单 Reducer 可能 OOM。优化方式:
SELECT * FROM (
-- 第一阶段:多个 Reducer 各自求局部 Top5
SELECT * FROM person SORT BY age DESC LIMIT 5
) t
-- 第二阶段:1 个 Reducer 对局部 Top5 求全局 Top5
ORDER BY age DESC LIMIT 5;
假设原表 1 亿条,开 10 个 Reducer,第一阶段输出 50 条——第二阶段只需对这 50 条排序,压力从"1 亿条 → 1 个 Reducer"变成了"50 条 → 1 个 Reducer"。
四大排序对比总结
| 排序方式 | 作用范围 | Reducer 数量 | 结果 |
|---|---|---|---|
| ORDER BY | 全局排序 | 强制 = 1 | 结果绝对正确,但数据量大时可能 OOM |
| SORT BY | 局部排序(每个 Reducer 内) | 可多个 | 每个 Reducer 内部有序,全局无序 |
| DISTRIBUTE BY | 控制数据分发到哪个 Reducer | 可多个 | 类似自定义分区器,常搭配 SORT BY |
| CLUSTER BY | = DISTRIBUTE BY + SORT BY(字段相同) | 可多个 | 分桶表的默认方式 |
-- DISTRIBUTE BY + SORT BY 可以不同字段
SELECT * FROM person
DISTRIBUTE BY dept -- 按部门分发
SORT BY age DESC; -- 每个部门内按年龄排序
-- CLUSTER BY = DISTRIBUTE BY + SORT BY(同一字段)
SELECT * FROM person CLUSTER BY dept;
-- 等价于:
SELECT * FROM person DISTRIBUTE BY dept SORT BY dept;
十、存储格式专题
10.1 五种存储格式对比
| 格式 | 存储方式 | 压缩比 | 查询性能 | 是否可读 | 适用场景 |
|---|---|---|---|---|---|
| TEXTFILE | 行式存储 | 低 | 低 | ✅ 可读 | 原始数据接入(ODS 层) |
| SEQUENCEFILE | 行式(二进制键值对) | 中 | 中 | ❌ | 合并小文件 |
| ORC | 列式存储 | ⭐ 最高 | ⭐ 最佳 | ❌ | Hive 内部表首选 |
| Parquet | 列式存储 | 高 | 高 | ❌ | 多引擎共享(Hive+Spark+Impala) |
| Avro | 行式 + Schema 自带 | 中 | 中 | ❌ | 表结构频繁变化场景 |
10.2 ORC vs Parquet — 怎么选?
| 维度 | ORC | Parquet |
|---|---|---|
| 起源 | Hive 团队(来自 Hive 的列式优化) | Twitter + Cloudera |
| Hive 兼容性 | ⭐ 最佳(Hive 原生支持,谓词下推等优化最好) | 良好 |
| 跨引擎 | 主要在 Hive 生态使用 | ⭐ 最佳(Spark/Impala/Presto/Drill 都原生支持) |
| 压缩比 | ⭐ 略高 | 高 |
| 适用场景 | 纯 Hive 环境 | 多计算引擎混合环境 |
10.3 企业级最佳实践流水线
原始数据接入(CSV/JSON/日志)→ 存为 TEXTFILE 外部表
│
▼ ETL 清洗(INSERT ... SELECT)
中间加工结果 → 存为 ORC 或 Parquet 内部表
│
▼ HQL 分析查询
复杂报表 / 即席查询 → 在 ORC/Parquet 表上跑
-- Step 1:原始数据 TEXTFILE 外部表
CREATE EXTERNAL TABLE ods_log (
ip STRING,
ts TIMESTAMP,
url STRING
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE
LOCATION '/data/raw_log/';
-- Step 2:ETL 后写入 ORC 表(查询性能提升数倍)
CREATE TABLE dwd_log_clean (
ip STRING,
ts TIMESTAMP,
url STRING
)
STORED AS ORC;
INSERT OVERWRITE TABLE dwd_log_clean
SELECT * FROM ods_log WHERE url IS NOT NULL;
十一、篇末小结
Hive 核心知识梳理
Hive 知识链路:
数据仓库概念
├── 面向主题 / 集成 / 随时间变化 / 相对稳定
├── 数据仓库四层架构(数据源→ETL→OLAP→前端)
└── 数据模型(星型 vs 雪花)
│
Hive 架构
├── 用户接口 → Thrift → 驱动引擎 → Metastore
├── 内部表 vs 外部表(LOCATION 目录机制)
├── 分区表(目录层级)vs 桶表(文件 Hash)
│
DDL 数据定义
├── CREATE / ALTER / DROP DATABASE / TABLE
├── PARTITIONED BY / CLUSTERED BY / STORED AS
└── ROW FORMAT(分隔符定义)
│
DML 数据操作
├── LOAD DATA(内部表)vs LOCATION(外部表)
├── INSERT INTO(追加)vs INSERT OVERWRITE(覆盖)
├── SELECT / JOIN / GROUP BY
├── ORDER BY(全局)vs SORT BY(局部)
└── DISTRIBUTE BY / CLUSTER BY
│
存储格式
├── TEXTFILE → ORC / Parquet 的优化流水线
└── 列式存储优势(高压缩 + 谓词下推)
一句话总结
Hive = 把 SQL 翻译成 MapReduce 的工具。它让你用熟悉的 SQL 语言处理海量数据,底层自动完成分布式计算。
后记:五篇系列至此完结。从云计算与虚拟化的底层原理,到 HDFS 的存储架构,再到 MapReduce 的分布式计算,最后用 Hive 把一切封装成 SQL——这就是大数据技术体系从基础设施到上层应用的全链路。希望这五篇文章能帮你建立起清晰的知识框架。如果你有任何疑问或想深入了解某个主题,欢迎留言讨论!
更多推荐
所有评论(0)