Apache Hive 完整知识总结与使用教程
版本参考 :Apache Hive 4.x / 3.1.x(当前主流生产版本)
官方网站 :https://hive.apache.org/
官方文档:https://cwiki.apache.org/confluence/display/Hive/Home
目录
- [Hive 概述](#Hive 概述)
- [Hive 核心架构](#Hive 核心架构)
- [Hive 数据模型](#Hive 数据模型)
- 安装与环境配置
- 4.1 前置条件
- 4.2 二进制包安装(传统方式)
- 4.3 配置 MySQL 作为 Metastore
- 4.4 启动 Hive 服务
- 4.5 Docker 快速启动(推荐入门)
- [HiveQL DDL 数据定义语言](#HiveQL DDL 数据定义语言)
- 5.1 数据库操作
- 5.2 表的创建(内部表 vs 外部表)
- 5.3 分区表
- 5.4 分桶表
- 5.5 表结构修改
- 5.6 视图(View)
- [HiveQL DML 数据操作语言](#HiveQL DML 数据操作语言)
- 6.1 数据加载(LOAD DATA)
- 6.2 INSERT 语句
- 6.3 SELECT 查询详解
- 6.4 JOIN 操作
- 6.5 子查询与 CTE
- 6.6 窗口函数
- 6.7 ACID 事务(UPDATE / DELETE)
- [Hive 数据类型详解](#Hive 数据类型详解)
- [Hive 文件格式与存储](#Hive 文件格式与存储)
- SerDe(序列化/反序列化)
- [Hive 内置函数大全](#Hive 内置函数大全)
- [用户自定义函数(UDF / UDAF / UDTF)](#用户自定义函数(UDF / UDAF / UDTF))
- [Hive 执行引擎](#Hive 执行引擎)
- [Hive 性能调优](#Hive 性能调优)
- [Hive 分区管理深入](#Hive 分区管理深入)
- [Hive 元数据服务(Metastore)](#Hive 元数据服务(Metastore))
- [HiveServer2 与 Beeline](#HiveServer2 与 Beeline)
- [Hive 安全机制](#Hive 安全机制)
- [Hive 与生态系统集成](#Hive 与生态系统集成)
- [Hive 4.x 新特性](#Hive 4.x 新特性)
- 常见问题与排查
- 最佳实践总结
- [Hive 与其他技术对比](#Hive 与其他技术对比)
- [附录:配置项速查 & 官方资源](#附录:配置项速查 & 官方资源)
1. Hive 概述
1.1 什么是 Apache Hive?
Apache Hive 是一个构建在 Apache Hadoop 之上的分布式数据仓库软件系统,由 Facebook 于 2007 年开发,后捐献给 Apache 软件基金会。
Hive 的核心价值在于:它提供了类 SQL 的查询语言 HiveQL(HQL),使得熟悉 SQL 的开发者和分析师无需学习 MapReduce 编程即可处理和分析存储在 HDFS 中的海量数据。HiveQL 在底层被自动翻译为 MapReduce、Apache Tez 或 Apache Spark 作业执行。
开发者写 HQL 查询 → Hive 编译优化 → 生成 MapReduce/Tez/Spark 作业 → 在 YARN 上执行 → 结果写回 HDFS
1.2 Hive 解决了什么问题?
| 传统 MapReduce 痛点 | Hive 的解法 |
|---|---|
| 必须用 Java 编写 MapReduce 程序 | HQL(类 SQL),门槛极低 |
| 开发周期长,调试困难 | 声明式查询,框架自动生成执行计划 |
| 无法方便地管理结构化数据的 Schema | Metastore 统一管理表结构、分区等元数据 |
| 缺乏数据发现和数据抽象机制 | 提供数据仓库级别的抽象:数据库、表、分区、视图 |
| 批量数据分析没有统一入口 | 标准 JDBC/ODBC 接口,BI 工具可直接对接 |
1.3 Hive 的核心特性
- SQL 接口:支持 SQL:2003、SQL:2011、SQL:2016 大量标准功能,降低大数据学习门槛
- 可扩展性:通过 UDF/UDAF/UDTF 支持自定义函数,通过 SerDe 扩展支持的数据格式
- 多执行引擎:支持 MapReduce、Apache Tez、Apache Spark 作为底层执行引擎
- 多存储格式:支持 TextFile、ORC、Parquet、Avro、RCFile 等多种文件格式
- 元数据管理:HMS(Hive Metastore Service)提供集中式元数据存储和管理
- 分区与分桶:通过分区(Partition)和分桶(Bucketing)优化查询性能
- ACID 支持:Hive 0.14+ 支持行级 INSERT/UPDATE/DELETE(需 ORC 格式 + 事务配置)
- Sub-second 查询:通过 LLAP(Live Long and Process)实现交互式亚秒级查询
- 多云兼容:支持 HDFS、Amazon S3、Azure ADLS、Google Cloud Storage 等存储后端
1.4 Hive 发展历程
| 版本里程碑 | 时间 | 重要变化 |
|---|---|---|
| Hive 0.x(初始版本) | 2008~2012 | Facebook 开源,基本 DDL/DML,仅支持 MapReduce |
| Hive 0.13 | 2014 | 原生支持 Parquet 格式 |
| Hive 0.14 | 2014 | ACID 事务支持(UPDATE/DELETE),ORC 格式 |
| Hive 2.0 | 2016 | LLAP(交互式查询),HiveServer2 改进,Hive CLI 弃用 |
| Hive 3.0 | 2018 | 移除索引,ACID 默认开启,Druid 集成,去除旧 API |
| Hive 3.1.x | 2019~2022 | 主流生产版本,稳定性大幅提升 |
| Hive 4.0 | 2024 | JDK 21 支持、Iceberg v3 深度集成、REST Catalog、删除向量 |
| Hive 4.2.0 | 2025 | 当前最新版,增强 Iceberg 功能、自动 compaction |
1.5 Hive 适用场景 vs 不适用场景
✅ 适用场景:
- 大规模离线数据仓库(ETL、报表、数据分析)
- PB 级数据的批量处理
- 数据湖架构中的结构化查询层
- BI 工具(Tableau、Power BI 等)通过 JDBC 对接大数据平台
- 数据迁移和数据质量检查
❌ 不适用场景:
- 在线事务处理(OLTP):延迟高(秒到分钟级),不适合实时交易
- 低延迟查询(<100ms 响应):应使用 HBase、Druid、ClickHouse
- 频繁的小数据量增删改:ACID 支持有限,效率低
- 实时流数据处理:应使用 Apache Flink 或 Spark Streaming
2. Hive 核心架构
2.1 整体架构图
┌──────────────────────────────────────────────────────────────────┐
│ Client 层 │
│ CLI(hive shell) | Beeline(JDBC/ODBC)| Web UI | JDBC/ODBC │
└───────────────────────────────┬──────────────────────────────────┘
│ HQL 查询
▼
┌──────────────────────────────────────────────────────────────────┐
│ HiveServer2 服务层 │
│ Thrift Server(处理远程连接,支持多并发认证) │
└───────────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Driver │ │ Metastore│ │ Compiler │
│(会话管理)│◄─────────►│(元数据) │◄─────────►│(查询解析)│
└──────────┘ └──────────┘ └──────────┘
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ RDBMS(MySQL等) │ │
│ │ (持久化元数据) │ │
│ └──────────────────┘ │
│ │
▼ ┌────────────────────► │
┌──────────┐ │ ▼
│Optimizer │◄────────────────┘ ┌──────────────┐
│(查询优化)│ │ Execution │
└──────────┘ │ Engine │
│ MapReduce / │
│ Tez /Spark │
└──────┬───────┘
│
┌───────────────────────┤
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ YARN │ │ HDFS │
│(资源调度)│ │(数据存储)│
└──────────┘ └──────────┘
2.2 核心组件详解
① UI(用户界面)
用户提交 HQL 查询和操作的入口,包括:
- Hive CLI:命令行工具,已在 Hive 2.0 后被弃用,生产不推荐使用
- Beeline:基于 JDBC 的客户端 CLI,通过 HiveServer2 连接,推荐使用
- HWI(Web Interface):Web 界面,已废弃
- JDBC/ODBC:通过 HiveServer2 对接 BI 工具和应用程序
② Driver(驱动)
负责管理 HQL 语句的完整生命周期:
- 接收来自 UI 的 HQL 语句
- 创建和管理 Session Handle(会话句柄)
- 提供 JDBC/ODBC 兼容接口
- 协调 Compiler、Optimizer、Executor 的工作
- 管理执行进度和结果获取
③ Compiler(编译器)
HQL 语句的解析与编译核心:
-
词法分析(Lexical Analysis):将 HQL 文本解析为 Token 序列
-
语法分析(Parsing) :生成 AST(Abstract Syntax Tree,抽象语法树)
-
语义分析(Semantic Analysis):检查表名、列名、数据类型的合法性(查询 Metastore)
-
逻辑计划生成(Logical Plan):将 AST 转换为算子树(Operator Tree)
-
执行计划生成(Physical Plan) :将逻辑计划转换为 DAG(有向无环图),分解为 MapReduce/Tez/Spark 的 Stage
HQL 文本 → Token → AST → Operator Tree → DAG of Stages → 物理执行计划
④ Optimizer(优化器)
对编译器生成的执行计划进行变换优化:
- 谓词下推(Predicate Pushdown):将过滤条件尽早执行,减少数据量
- 列裁剪(Column Pruning):只读取查询中用到的列
- 分区裁剪(Partition Pruning):根据分区条件跳过无关分区
- Join 重排序(Join Reordering):将小表放在 Join 的驱动侧
- 公共子表达式消除:避免重复计算
- 使用 CBO(Cost-Based Optimizer):基于统计信息(ANALYZE TABLE)选择最优计划
⑤ Metastore(元数据存储)
Hive 最重要的基础设施之一,存储所有表和分区的元数据:
| 元数据类型 | 内容 |
|---|---|
| 数据库信息 | 数据库名、描述、HDFS 路径 |
| 表信息 | 表名、列名、数据类型、注释、HDFS 位置、存储格式 |
| 分区信息 | 分区键、分区值、对应 HDFS 路径 |
| SerDe 信息 | 序列化/反序列化类及属性 |
| 统计信息 | 行数、列的 distinct 值数、最大/最小值等(ANALYZE TABLE 生成) |
Metastore 将元数据存储在关系型数据库(默认 Derby,生产用 MySQL/PostgreSQL)中,通过 Thrift 服务对外暴露。
⑥ Execution Engine(执行引擎)
将优化后的执行计划提交给底层计算框架执行:
- 与 YARN ResourceManager 交互,申请计算资源
- 监控 Job 执行进度
- 处理 Task 失败重试
- 支持 MapReduce、Tez、Spark 三种引擎(可在会话级别切换)
3. Hive 数据模型
Hive 的数据模型层次从大到小为:Database → Table → Partition → Bucket
3.1 Database(数据库)
类似于关系型数据库中的 Schema,是表的命名空间。
sql
-- 创建数据库(在 HDFS 中对应一个目录 /user/hive/warehouse/<db_name>.db/)
CREATE DATABASE IF NOT EXISTS sales_db
COMMENT 'Sales data warehouse'
LOCATION '/data/hive/sales'
WITH DBPROPERTIES ('creator'='admin', 'date'='2024-01-01');
3.2 Table(表)
Hive 中有两种核心表类型:
内部表(Managed Table)
默认表类型,Hive 完全管理数据的生命周期。
DROP TABLE 时,HDFS 上的数据文件也会被删除。
数据存储在 hive.metastore.warehouse.dir 配置的目录下(默认 /user/hive/warehouse/<db>/<table>/)。
外部表(External Table)
Hive 只管理元数据,不管理实际数据。
DROP TABLE 时,只删除元数据,HDFS 上的数据文件不受影响。
适合数据在 HDFS/S3 上已经存在、由其他工具管理的场景。
3.3 Partition(分区)
将表数据按某一列(分区键)的值划分到不同的 HDFS 子目录中,查询时通过分区裁剪跳过不相关数据:
/user/hive/warehouse/orders/
dt=2024-01-01/ ← 分区目录
dt=2024-01-02/
dt=2024-01-03/
分区列不存储在数据文件中,而是体现在目录结构里。
3.4 Bucket(桶/分桶)
在分区(或表)的基础上,进一步将数据按某列的哈希值分散到固定数量的文件(桶)中:
/user/hive/warehouse/orders/dt=2024-01-01/
000000_0 ← 桶 0
000001_0 ← 桶 1
000002_0 ← 桶 2
000003_0 ← 桶 3
分桶的好处:
- 高效 Bucket JOIN:两张表按相同列以相同桶数分桶,Join 时无需全表扫描
- 高效 TABLESAMPLE:按桶抽样,效率远高于 RAND() 抽样
- 数据均匀分布:避免数据倾斜
4. 安装与环境配置
4.1 前置条件
| 依赖 | 版本要求 |
|---|---|
| 操作系统 | Linux(Ubuntu 20.04+ / CentOS 7+) |
| Java | JDK 8(Hive 3.x)或 JDK 11/17/21(Hive 4.x) |
| Hadoop | 已安装运行的 Hadoop 集群(HDFS + YARN) |
| 数据库 | Derby(测试)/ MySQL 5.7+(生产)/ PostgreSQL 12+(生产) |
| 内存 | 推荐 ≥ 8GB RAM |
4.2 二进制包安装(传统方式)
步骤一:下载并解压 Hive
bash
# 从 https://hive.apache.org/downloads.html 获取最新版本
# 以 Hive 3.1.3 为例
wget https://downloads.apache.org/hive/hive-3.1.3/apache-hive-3.1.3-bin.tar.gz
tar -xzf apache-hive-3.1.3-bin.tar.gz
sudo mv apache-hive-3.1.3-bin /usr/local/hive
步骤二:配置环境变量
编辑 ~/.bashrc 或 /etc/profile,追加:
bash
export HIVE_HOME=/usr/local/hive
export PATH=$PATH:$HIVE_HOME/bin
export HIVE_CONF_DIR=$HIVE_HOME/conf
bash
source ~/.bashrc
hive --version # 验证安装
步骤三:创建 HDFS 仓库目录
bash
hdfs dfs -mkdir -p /user/hive/warehouse
hdfs dfs -mkdir -p /tmp/hive
hdfs dfs -chmod 777 /user/hive/warehouse
hdfs dfs -chmod 777 /tmp/hive
步骤四:配置 hive-site.xml
bash
cp $HIVE_HOME/conf/hive-default.xml.template $HIVE_HOME/conf/hive-site.xml
vim $HIVE_HOME/conf/hive-site.xml
最小化配置(使用嵌入式 Derby,仅用于测试):
xml
<configuration>
<!-- Hive 数据仓库在 HDFS 中的位置 -->
<property>
<name>hive.metastore.warehouse.dir</name>
<value>/user/hive/warehouse</value>
</property>
<!-- Hive 临时文件目录 -->
<property>
<name>hive.exec.scratchdir</name>
<value>/tmp/hive</value>
</property>
<!-- 执行引擎(mr | tez | spark) -->
<property>
<name>hive.execution.engine</name>
<value>tez</value>
</property>
<!-- 启用动态分区 -->
<property>
<name>hive.exec.dynamic.partition</name>
<value>true</value>
</property>
<property>
<name>hive.exec.dynamic.partition.mode</name>
<value>nonstrict</value>
</property>
</configuration>
步骤五:初始化 Schema(使用 Derby)
bash
# 初始化 Derby 元数据 Schema(只需执行一次)
schematool -dbType derby -initSchema
# 验证
hive
hive> SHOW DATABASES;
4.3 配置 MySQL 作为 Metastore(生产推荐)
步骤一:安装 MySQL 并创建元数据库
bash
sudo apt install -y mysql-server
sudo mysql_secure_installation
# 登录 MySQL 创建用户和数据库
mysql -u root -p
sql
-- 在 MySQL 中执行
CREATE DATABASE metastore DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
CREATE USER 'hive'@'%' IDENTIFIED BY 'hive_password';
GRANT ALL PRIVILEGES ON metastore.* TO 'hive'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
EXIT;
步骤二:添加 MySQL JDBC 驱动
bash
# 下载 MySQL Connector/J
wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.33.tar.gz
tar -xzf mysql-connector-java-8.0.33.tar.gz
cp mysql-connector-java-8.0.33/mysql-connector-java-8.0.33.jar $HIVE_HOME/lib/
步骤三:修改 hive-site.xml 指向 MySQL
xml
<configuration>
<!-- Hive 仓库目录 -->
<property>
<name>hive.metastore.warehouse.dir</name>
<value>/user/hive/warehouse</value>
</property>
<!-- MySQL 连接信息 -->
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://localhost:3306/metastore?createDatabaseIfNotExist=true&useSSL=false&serverTimezone=UTC</value>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.cj.jdbc.Driver</value>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>hive</value>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>hive_password</value>
</property>
<!-- Metastore 服务地址(远程模式) -->
<property>
<name>hive.metastore.uris</name>
<value>thrift://localhost:9083</value>
</property>
<!-- HiveServer2 配置 -->
<property>
<name>hive.server2.thrift.port</name>
<value>10000</value>
</property>
<property>
<name>hive.server2.thrift.bind.host</name>
<value>0.0.0.0</value>
</property>
<!-- 关闭 doAs(单用户测试时) -->
<property>
<name>hive.server2.enable.doAs</name>
<value>false</value>
</property>
<!-- 动态分区配置 -->
<property>
<name>hive.exec.dynamic.partition</name>
<value>true</value>
</property>
<property>
<name>hive.exec.dynamic.partition.mode</name>
<value>nonstrict</value>
</property>
</configuration>
步骤四:初始化 MySQL Metastore Schema
bash
schematool -dbType mysql -initSchema
# 验证 schema 版本
schematool -dbType mysql -info
4.4 启动 Hive 服务
bash
# 1. 确保 Hadoop 已启动
jps | grep -E "NameNode|DataNode|ResourceManager|NodeManager"
# 2. 启动 Metastore 服务(后台运行)
hive --service metastore &
# 或
nohup hive --service metastore > /tmp/metastore.log 2>&1 &
# 验证 Metastore 是否在 9083 端口监听
netstat -tuln | grep 9083
# 3. 启动 HiveServer2(后台运行)
hive --service hiveserver2 &
# 或
nohup hive --service hiveserver2 > /tmp/hiveserver2.log 2>&1 &
# 验证 HiveServer2 是否在 10000 端口监听
netstat -tuln | grep 10000
# 4. 访问 HiveServer2 Web UI
# http://localhost:10002
4.5 Docker 快速启动(入门推荐)
这是最快速的入门方式,官方提供完整 Docker 镜像:
bash
export HIVE_VERSION=4.0.1
# 方式一:最简单,使用嵌入式 Derby,数据不持久化(快速体验)
docker run -d -p 10000:10000 -p 10002:10002 \
--env SERVICE_NAME=hiveserver2 \
--name hive4 \
apache/hive:${HIVE_VERSION}
# 连接到 Beeline
docker exec -it hive4 beeline -u 'jdbc:hive2://localhost:10000/'
bash
# 方式二:使用 docker-compose,HiveServer2 + Metastore + PostgreSQL(数据持久化)
# 创建 compose.yaml 文件
cat > compose.yaml << 'EOF'
version: "3"
services:
postgres:
image: postgres:17.2
environment:
POSTGRES_PASSWORD: "hivepassword"
POSTGRES_DB: "metastore"
volumes:
- postgres-data:/var/lib/postgresql/data
metastore:
image: apache/hive:4.0.1
depends_on:
- postgres
environment:
SERVICE_NAME: metastore
DB_DRIVER: postgres
SERVICE_OPTS: >-
-Djavax.jdo.option.ConnectionDriverName=org.postgresql.Driver
-Djavax.jdo.option.ConnectionURL=jdbc:postgresql://postgres:5432/metastore
-Djavax.jdo.option.ConnectionUserName=postgres
-Djavax.jdo.option.ConnectionPassword=hivepassword
ports:
- "9083:9083"
hiveserver2:
image: apache/hive:4.0.1
depends_on:
- metastore
environment:
SERVICE_NAME: hiveserver2
IS_RESUME: "true"
SERVICE_OPTS: "-Dhive.metastore.uris=thrift://metastore:9083"
ports:
- "10000:10000"
- "10002:10002"
volumes:
- warehouse:/opt/hive/data/warehouse
volumes:
postgres-data:
warehouse:
EOF
docker-compose up -d
# 连接 Beeline
docker exec -it hiveserver2-container beeline -u 'jdbc:hive2://localhost:10000/'
5. HiveQL DDL 数据定义语言
5.1 数据库操作
sql
-- 创建数据库
CREATE DATABASE IF NOT EXISTS mydb
COMMENT '我的数据仓库'
LOCATION '/data/hive/mydb'
WITH DBPROPERTIES ('owner'='dataeng', 'version'='1.0');
-- 查看数据库列表
SHOW DATABASES;
SHOW DATABASES LIKE 'my*'; -- 支持通配符
-- 查看数据库详情
DESCRIBE DATABASE EXTENDED mydb;
-- 切换数据库
USE mydb;
-- 删除数据库(CASCADE 同时删除表)
DROP DATABASE IF EXISTS mydb CASCADE;
-- 修改数据库属性
ALTER DATABASE mydb SET DBPROPERTIES ('updated_by'='admin');
5.2 表的创建(内部表 vs 外部表)
完整的 CREATE TABLE 语法
sql
CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name
(
col_name data_type [COMMENT 'column comment'],
...
)
[COMMENT 'table comment']
[PARTITIONED BY (col_name data_type [COMMENT ...], ...)]
[CLUSTERED BY (col_name, ...) [SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS]
[ROW FORMAT row_format]
[STORED AS file_format]
[LOCATION 'hdfs_path']
[TBLPROPERTIES (property_name=property_value, ...)]
[AS select_statement]
内部表示例
sql
-- 基本内部表
CREATE TABLE IF NOT EXISTS employees (
emp_id INT COMMENT '员工ID',
name STRING COMMENT '姓名',
dept STRING COMMENT '部门',
salary DOUBLE COMMENT '薪资',
hire_date DATE COMMENT '入职日期',
skills ARRAY<STRING> COMMENT '技能列表',
address STRUCT<city:STRING, zip:STRING> COMMENT '地址'
)
COMMENT '员工信息表'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
COLLECTION ITEMS TERMINATED BY ','
MAP KEYS TERMINATED BY ':'
LINES TERMINATED BY '\n'
STORED AS TEXTFILE
TBLPROPERTIES ('author'='admin', 'create_time'='2024-01-01');
-- 使用 ORC 格式(推荐生产使用)
CREATE TABLE orders_orc (
order_id BIGINT,
user_id INT,
amount DECIMAL(10,2),
create_time TIMESTAMP
)
STORED AS ORC
TBLPROPERTIES ('orc.compress'='SNAPPY');
-- 使用 CTAS(Create Table As Select)从已有表创建
CREATE TABLE dept_summary
STORED AS ORC
AS
SELECT dept, COUNT(*) AS cnt, AVG(salary) AS avg_salary
FROM employees
GROUP BY dept;
外部表示例
sql
-- 创建外部表,指向 HDFS 上已有数据
CREATE EXTERNAL TABLE web_logs (
ip STRING,
log_time STRING,
request STRING,
status INT,
bytes BIGINT
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ' '
STORED AS TEXTFILE
LOCATION '/data/weblogs/2024/'
TBLPROPERTIES ('skip.header.line.count'='1'); -- 跳过 CSV 头行
-- 外部表(Parquet 格式,指向 S3)
CREATE EXTERNAL TABLE s3_events (
event_id STRING,
event_type STRING,
user_id BIGINT,
event_time TIMESTAMP
)
STORED AS PARQUET
LOCATION 's3a://my-bucket/events/';
查看表信息
sql
SHOW TABLES; -- 列出所有表
SHOW TABLES LIKE 'order*'; -- 模糊查找
DESCRIBE employees; -- 查看表结构(列名、类型)
DESCRIBE FORMATTED employees; -- 查看详细信息(包括 HDFS 路径、格式等)
DESCRIBE EXTENDED employees; -- 查看更详细信息(包括原始元数据)
SHOW CREATE TABLE employees; -- 查看建表语句
SHOW TBLPROPERTIES employees; -- 查看表属性
5.3 分区表
sql
-- 创建分区表(按日期分区)
CREATE TABLE user_events (
user_id BIGINT,
event_type STRING,
event_time TIMESTAMP,
page_url STRING
)
PARTITIONED BY (
dt STRING COMMENT '日期分区,格式 yyyy-MM-dd',
country STRING COMMENT '国家分区'
)
STORED AS ORC;
-- 手动添加分区(静态分区)
ALTER TABLE user_events ADD PARTITION (dt='2024-01-01', country='CN')
LOCATION '/data/events/dt=2024-01-01/country=CN';
-- 添加多个分区
ALTER TABLE user_events ADD
PARTITION (dt='2024-01-02', country='CN')
PARTITION (dt='2024-01-02', country='US');
-- 删除分区(同时删除数据,如果是内部表)
ALTER TABLE user_events DROP PARTITION (dt='2023-12-01');
ALTER TABLE user_events DROP PARTITION (dt < '2023-01-01'); -- 按条件批量删除
-- 查看分区列表
SHOW PARTITIONS user_events;
SHOW PARTITIONS user_events PARTITION (dt='2024-01-01');
-- 修复分区(当 HDFS 目录已存在但 Metastore 没有记录时)
MSCK REPAIR TABLE user_events;
-- 查询时分区裁剪(必须加分区条件,否则全表扫描)
SELECT COUNT(*) FROM user_events WHERE dt='2024-01-01' AND country='CN';
5.4 分桶表
sql
-- 创建分桶表(按 user_id 哈希分为 32 个桶)
CREATE TABLE user_orders (
order_id BIGINT,
user_id INT,
amount DOUBLE,
status STRING
)
CLUSTERED BY (user_id) SORTED BY (order_id ASC) INTO 32 BUCKETS
STORED AS ORC;
-- 同时使用分区和分桶
CREATE TABLE user_behavior (
user_id BIGINT,
action STRING,
item_id BIGINT
)
PARTITIONED BY (dt STRING)
CLUSTERED BY (user_id) INTO 256 BUCKETS
STORED AS ORC;
-- 向分桶表插入数据(必须启用 bucketing 支持)
SET hive.enforce.bucketing = true; -- Hive 1.x 需要,2.x+ 已默认
INSERT INTO user_orders SELECT order_id, user_id, amount, status FROM raw_orders;
-- 分桶采样查询
SELECT * FROM user_orders TABLESAMPLE(BUCKET 1 OUT OF 32 ON user_id); -- 取第1个桶
SELECT * FROM user_orders TABLESAMPLE(BUCKET 1 OUT OF 8 ON user_id); -- 取前4个桶(32/8=4)
5.5 表结构修改
sql
-- 重命名表
ALTER TABLE employees RENAME TO staff;
-- 添加列
ALTER TABLE employees ADD COLUMNS (
phone STRING COMMENT '手机号',
email STRING COMMENT '邮箱'
);
-- 修改列(改名/改类型)
ALTER TABLE employees CHANGE COLUMN name full_name STRING COMMENT '全名';
-- 替换所有列(重新定义列结构)
ALTER TABLE employees REPLACE COLUMNS (
emp_id INT,
full_name STRING,
salary DOUBLE
);
-- 修改表属性
ALTER TABLE employees SET TBLPROPERTIES ('last_modified_by'='admin');
-- 修改存储格式
ALTER TABLE employees SET FILEFORMAT ORC;
-- 修改表位置
ALTER TABLE employees SET LOCATION '/new/hdfs/path/';
-- 分区表修改分区属性
ALTER TABLE user_events PARTITION (dt='2024-01-01') SET FILEFORMAT ORC;
-- 合并小文件(ORC 格式支持)
ALTER TABLE user_events PARTITION (dt='2024-01-01') CONCATENATE;
5.6 视图(View)
sql
-- 创建视图
CREATE VIEW IF NOT EXISTS active_employees AS
SELECT emp_id, name, dept, salary
FROM employees
WHERE hire_date > '2020-01-01' AND salary > 5000;
-- 查询视图(和查询普通表一样)
SELECT dept, COUNT(*) FROM active_employees GROUP BY dept;
-- 修改视图
ALTER VIEW active_employees AS
SELECT emp_id, name, dept, salary, hire_date
FROM employees
WHERE salary > 8000;
-- 删除视图
DROP VIEW IF EXISTS active_employees;
-- 物化视图(Hive 3.0+,需要 ACID)
CREATE MATERIALIZED VIEW emp_dept_summary
AS SELECT dept, COUNT(*) cnt, AVG(salary) avg_sal
FROM employees GROUP BY dept;
-- 重建物化视图(更新缓存)
ALTER MATERIALIZED VIEW emp_dept_summary REBUILD;
6. HiveQL DML 数据操作语言
6.1 数据加载(LOAD DATA)
sql
-- 从本地文件系统加载(LOCAL 关键字表示本地文件)
-- 注意:会将文件移动/复制到 HDFS 仓库目录
LOAD DATA LOCAL INPATH '/home/user/data/employees.csv'
INTO TABLE employees;
-- 加载并覆盖现有数据
LOAD DATA LOCAL INPATH '/home/user/data/employees.csv'
OVERWRITE INTO TABLE employees;
-- 从 HDFS 加载(不加 LOCAL,文件从 HDFS 移动到表目录)
LOAD DATA INPATH '/data/raw/employees.csv'
INTO TABLE employees;
-- 加载数据到特定分区
LOAD DATA LOCAL INPATH '/data/events_20240101.csv'
INTO TABLE user_events PARTITION (dt='2024-01-01', country='CN');
注意:LOAD DATA 是纯文件移动操作,不进行 MapReduce,速度很快。它不会验证数据格式和类型。
6.2 INSERT 语句
sql
-- INSERT INTO(追加数据)
INSERT INTO TABLE employees VALUES
(1001, 'Alice', 'Engineering', 12000.00, '2022-03-15'),
(1002, 'Bob', 'Marketing', 9500.00, '2021-07-01');
-- INSERT OVERWRITE(覆盖数据)
INSERT OVERWRITE TABLE dept_summary
SELECT dept, COUNT(*) cnt, AVG(salary) avg_sal
FROM employees GROUP BY dept;
-- 动态分区插入(自动根据查询结果创建分区)
SET hive.exec.dynamic.partition.mode = nonstrict;
INSERT OVERWRITE TABLE user_events PARTITION (dt, country)
SELECT user_id, event_type, event_time, page_url, dt, country
FROM raw_events
WHERE dt >= '2024-01-01';
-- 多目标插入(一次扫描,写多张表,性能优化技巧)
FROM employees
INSERT OVERWRITE TABLE high_salary_emp
SELECT * WHERE salary > 15000
INSERT INTO TABLE mid_salary_emp
SELECT * WHERE salary BETWEEN 8000 AND 15000
INSERT INTO TABLE low_salary_emp
SELECT * WHERE salary < 8000;
-- INSERT INTO ... SELECT(从查询结果插入)
INSERT INTO TABLE orders_backup
SELECT * FROM orders WHERE YEAR(create_time) = 2023;
6.3 SELECT 查询详解
sql
-- 基本查询
SELECT emp_id, name, salary
FROM employees
WHERE dept = 'Engineering' AND salary > 10000
ORDER BY salary DESC
LIMIT 10;
-- 聚合查询
SELECT dept,
COUNT(*) AS employee_count,
AVG(salary) AS avg_salary,
MAX(salary) AS max_salary,
MIN(salary) AS min_salary,
SUM(salary) AS total_salary
FROM employees
GROUP BY dept
HAVING COUNT(*) > 5
ORDER BY avg_salary DESC;
-- DISTINCT
SELECT DISTINCT dept FROM employees;
SELECT COUNT(DISTINCT dept) FROM employees; -- 统计唯一部门数
-- 正则表达式查询(REGEXP / RLIKE)
SELECT name FROM employees WHERE name RLIKE '^A.*';
SELECT name FROM employees WHERE name REGEXP 'Alice|Bob';
-- LIKE 模糊查询
SELECT name FROM employees WHERE name LIKE 'A%';
SELECT name FROM employees WHERE name LIKE '_ob'; -- 下划线匹配单个字符
-- BETWEEN ... AND
SELECT * FROM employees WHERE salary BETWEEN 8000 AND 12000;
-- IN 子句
SELECT * FROM employees WHERE dept IN ('Engineering', 'Marketing', 'Finance');
-- IS NULL / IS NOT NULL
SELECT * FROM employees WHERE email IS NULL;
-- CASE WHEN
SELECT name,
salary,
CASE
WHEN salary >= 15000 THEN '高薪'
WHEN salary >= 10000 THEN '中薪'
ELSE '低薪'
END AS salary_level
FROM employees;
-- SORT BY vs ORDER BY vs DISTRIBUTE BY vs CLUSTER BY
-- ORDER BY: 全局排序(只有一个 Reducer),大数据量慎用
SELECT * FROM employees ORDER BY salary DESC;
-- SORT BY: 每个 Reducer 内部排序(局部有序)
SELECT * FROM employees SORT BY salary DESC;
-- DISTRIBUTE BY: 按指定列分发到 Reducer(常与 SORT BY 配合)
SELECT * FROM employees DISTRIBUTE BY dept SORT BY dept, salary DESC;
-- CLUSTER BY: 等价于 DISTRIBUTE BY col SORT BY col(相同列升序)
SELECT * FROM employees CLUSTER BY dept;
-- LIMIT 与 OFFSET
SELECT * FROM employees ORDER BY salary DESC LIMIT 10;
SELECT * FROM employees ORDER BY salary DESC LIMIT 10 OFFSET 20; -- 第 21-30 条
-- TABLESAMPLE 数据采样
SELECT * FROM employees TABLESAMPLE(10 PERCENT); -- 随机采样 10%
SELECT * FROM employees TABLESAMPLE(1000 ROWS); -- 采样 1000 行
SELECT * FROM user_orders TABLESAMPLE(BUCKET 2 OUT OF 32 ON user_id); -- 桶采样
-- LATERAL VIEW EXPLODE(展开数组/Map 列)
SELECT emp_id, name, skill
FROM employees
LATERAL VIEW EXPLODE(skills) tmp AS skill;
-- LATERAL VIEW json_tuple(解析 JSON)
SELECT base.id, jt.name, jt.age
FROM json_table base
LATERAL VIEW json_tuple(base.json_col, 'name', 'age') jt AS name, age;
6.4 JOIN 操作
sql
-- INNER JOIN
SELECT e.name, e.salary, d.dept_name, d.location
FROM employees e
INNER JOIN departments d ON e.dept = d.dept_id;
-- LEFT OUTER JOIN
SELECT e.name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept = d.dept_id;
-- RIGHT OUTER JOIN
SELECT e.name, d.dept_name
FROM employees e
RIGHT JOIN departments d ON e.dept = d.dept_id;
-- FULL OUTER JOIN
SELECT e.name, d.dept_name
FROM employees e
FULL OUTER JOIN departments d ON e.dept = d.dept_id;
-- CROSS JOIN(笛卡尔积,谨慎使用!)
SELECT e.name, d.dept_name
FROM employees e CROSS JOIN departments d;
-- LEFT SEMI JOIN(相当于 EXISTS,效率高于子查询)
SELECT e.*
FROM employees e
LEFT SEMI JOIN high_salary_list h ON e.emp_id = h.emp_id;
-- MAP JOIN(小表 JOIN,广播到所有 Map 端,避免 Reduce)
-- 自动触发条件:hive.auto.convert.join=true 且小表 < hive.mapjoin.smalltable.filesize
SELECT /*+ MAPJOIN(d) */
e.name, d.dept_name
FROM employees e
JOIN departments d ON e.dept = d.dept_id;
-- Bucket Map Join(两张表均按相同列分桶时效率最高)
-- 需要设置:set hive.optimize.bucketmapjoin=true
6.5 子查询与 CTE
sql
-- 子查询(IN)
SELECT * FROM employees
WHERE dept IN (
SELECT dept_id FROM departments WHERE location = 'Beijing'
);
-- 子查询(EXISTS)
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM high_performers h WHERE h.emp_id = e.emp_id
);
-- FROM 子句中的子查询
SELECT dept, avg_sal
FROM (
SELECT dept, AVG(salary) AS avg_sal
FROM employees
GROUP BY dept
) t
WHERE avg_sal > 10000;
-- CTE(Common Table Expression,WITH 子句)------ Hive 0.13+
WITH
dept_stats AS (
SELECT dept, COUNT(*) cnt, AVG(salary) avg_sal
FROM employees
GROUP BY dept
),
high_dept AS (
SELECT dept FROM dept_stats WHERE avg_sal > 10000
)
SELECT e.name, e.salary, e.dept
FROM employees e
JOIN high_dept h ON e.dept = h.dept;
6.6 窗口函数(Window Functions)
窗口函数是 Hive 中非常强大的分析工具:
sql
-- 语法:function() OVER ([PARTITION BY ...] [ORDER BY ...] [frame_clause])
-- ROW_NUMBER:每行一个唯一序号
SELECT name, dept, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employees;
-- RANK:允许并列,跳号(1,1,3)
SELECT name, dept, salary,
RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS rnk
FROM employees;
-- DENSE_RANK:允许并列,不跳号(1,1,2)
SELECT name, dept, salary,
DENSE_RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS dense_rnk
FROM employees;
-- 取每个部门工资前3名(ROW_NUMBER 去重)
SELECT * FROM (
SELECT name, dept, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employees
) t WHERE rn <= 3;
-- LAG / LEAD:访问前/后 N 行数据
SELECT name, salary,
LAG(salary, 1, 0) OVER (PARTITION BY dept ORDER BY hire_date) AS prev_salary,
LEAD(salary, 1, 0) OVER (PARTITION BY dept ORDER BY hire_date) AS next_salary
FROM employees;
-- FIRST_VALUE / LAST_VALUE
SELECT name, dept, salary,
FIRST_VALUE(name) OVER (PARTITION BY dept ORDER BY salary DESC) AS top_earner
FROM employees;
-- SUM/AVG/COUNT 累计汇总
SELECT name, dept, salary,
SUM(salary) OVER (PARTITION BY dept ORDER BY hire_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total,
AVG(salary) OVER (PARTITION BY dept) AS dept_avg,
COUNT(*) OVER () AS total_employees
FROM employees;
-- NTILE:将数据分成 N 等份
SELECT name, salary,
NTILE(4) OVER (ORDER BY salary) AS quartile
FROM employees;
-- PERCENT_RANK / CUME_DIST
SELECT name, salary,
PERCENT_RANK() OVER (ORDER BY salary) AS pct_rank,
CUME_DIST() OVER (ORDER BY salary) AS cume_dist
FROM employees;
-- 帧规范(Frame Specification)
-- ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW → 从头到当前行
-- ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING → 前2行到后2行(滑动窗口)
-- RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING → 当前行到末尾
SELECT name, salary,
AVG(salary) OVER (
PARTITION BY dept
ORDER BY hire_date
ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING
) AS moving_avg
FROM employees;
6.7 ACID 事务(UPDATE / DELETE)
Hive 0.14+ 支持行级别的 UPDATE/DELETE,但需要满足以下条件:
sql
-- 前置配置(hive-site.xml 或 SET 命令)
SET hive.support.concurrency = true;
SET hive.enforce.bucketing = true;
SET hive.exec.dynamic.partition.mode = nonstrict;
SET hive.txn.manager = org.apache.hadoop.hive.ql.lockmgr.DbTxnManager;
SET hive.compactor.initiator.on = true;
SET hive.compactor.worker.threads = 1;
-- 建表必须使用 ORC 格式 + TRANSACTIONAL 属性
CREATE TABLE student (
id INT,
name STRING,
score INT
)
CLUSTERED BY (id) INTO 4 BUCKETS
STORED AS ORC
TBLPROPERTIES ('transactional'='true');
-- UPDATE 语句
UPDATE student SET score = 95 WHERE id = 1001;
UPDATE student SET name = 'Alice Zhang', score = score + 5 WHERE name = 'Alice';
-- DELETE 语句
DELETE FROM student WHERE id = 1005;
DELETE FROM student WHERE score < 60;
-- MERGE(Hive 2.2+,Upsert 操作)
MERGE INTO student AS target
USING updates AS source
ON target.id = source.id
WHEN MATCHED AND source.action = 'UPDATE' THEN
UPDATE SET name = source.name, score = source.score
WHEN MATCHED AND source.action = 'DELETE' THEN DELETE
WHEN NOT MATCHED THEN
INSERT VALUES (source.id, source.name, source.score);
7. Hive 数据类型详解
7.1 基本数据类型
| 类型 | 字节 | 范围/说明 | 示例 |
|---|---|---|---|
| TINYINT | 1B | -128 ~ 127 | 100Y |
| SMALLINT | 2B | -32768 ~ 32767 | 1000S |
| INT / INTEGER | 4B | -2^31 ~ 2^31-1 | 100 |
| BIGINT | 8B | -2^63 ~ 2^63-1 | 100L |
| FLOAT | 4B | 单精度浮点数 | 3.14 |
| DOUBLE | 8B | 双精度浮点数 | 3.14159265 |
| DECIMAL(p,s) | 可变 | 精确小数,p 总精度,s 小数位 | DECIMAL(10,2) |
| BOOLEAN | - | TRUE / FALSE | true |
| STRING | 可变 | 无长度限制的字符串(UTF-8) | 'hello' |
| VARCHAR(n) | 可变 | 最大长度 n 的字符串 | VARCHAR(255) |
| CHAR(n) | 固定 | 固定长度字符串,不足补空格 | CHAR(10) |
| BINARY | 可变 | 字节数组 | - |
| TIMESTAMP | - | 纳秒精度时间戳 | '2024-01-01 12:00:00' |
| DATE | - | 仅日期(无时间) | '2024-01-01' |
| INTERVAL | - | 时间间隔 | INTERVAL '1' DAY |
7.2 复杂数据类型
| 类型 | 语法 | 说明 | 示例 |
|---|---|---|---|
| ARRAY | ARRAY<data_type> |
有序列表,下标从 0 开始 | ['a','b','c'] |
| MAP | MAP<key_type, val_type> |
键值对,key 必须是基本类型 | {'k1':1,'k2':2} |
| STRUCT | STRUCT<name:type,...> |
命名字段的结构体 | {'name':'Alice','age':30} |
| UNIONTYPE | UNIONTYPE<t1,t2,...> |
联合类型(较少使用) | - |
sql
-- 复杂类型的使用示例
CREATE TABLE complex_example (
id INT,
tags ARRAY<STRING>, -- 数组
props MAP<STRING, STRING>, -- Map
address STRUCT<city:STRING, zip:STRING, country:STRING> -- 结构体
);
-- 插入复杂类型数据
INSERT INTO complex_example VALUES (
1,
ARRAY('hive', 'hadoop', 'spark'),
MAP('color', 'blue', 'size', 'L'),
NAMED_STRUCT('city', 'Beijing', 'zip', '100000', 'country', 'CN')
);
-- 访问复杂类型
SELECT
id,
tags[0] AS first_tag, -- 数组下标访问
tags[1] AS second_tag,
SIZE(tags) AS tag_count, -- 数组大小
props['color'] AS color, -- Map 键访问
address.city AS city, -- Struct 字段访问
address.zip AS zip
FROM complex_example;
-- EXPLODE 展开数组
SELECT id, tag
FROM complex_example
LATERAL VIEW EXPLODE(tags) tmp AS tag;
-- EXPLODE 展开 Map
SELECT id, k, v
FROM complex_example
LATERAL VIEW EXPLODE(props) tmp AS k, v;
7.3 类型转换
sql
-- 隐式转换(自动类型提升:TINYINT → SMALLINT → INT → BIGINT → FLOAT → DOUBLE)
SELECT 1 + 1.5; -- 自动转为 DOUBLE
-- 显式转换(CAST)
SELECT CAST('123' AS INT);
SELECT CAST(3.14 AS INT); -- 截断 → 3
SELECT CAST('2024-01-01' AS DATE);
SELECT CAST(salary AS STRING) FROM employees;
-- 字符串与数字转换
SELECT CAST('123abc' AS INT); -- 返回 NULL(无法转换)
-- 日期类型转换
SELECT TO_DATE('2024-01-15 12:30:00'); -- → '2024-01-15'
SELECT UNIX_TIMESTAMP('2024-01-01 00:00:00', 'yyyy-MM-dd HH:mm:ss'); -- → 秒级时间戳
SELECT FROM_UNIXTIME(1704067200, 'yyyy-MM-dd HH:mm:ss'); -- 时间戳 → 字符串
8. Hive 文件格式与存储
8.1 支持的文件格式对比
| 格式 | 存储方式 | 压缩支持 | 查询性能 | 写入性能 | 适用场景 |
|---|---|---|---|---|---|
| TextFile | 行式,纯文本 | gzip/bzip2 等 | 差 | 快 | 数据交换、调试、原始日志 |
| SequenceFile | 行式,二进制 | Block/Record | 一般 | 一般 | Hadoop 原生,历史遗留 |
| RCFile | 列式,行组 | 支持 | 中等 | 一般 | 旧系统 |
| ORC | 列式(带索引) | Zlib/Snappy/LZ4 | 极好 | 中等 | 生产推荐:分析型查询 |
| Parquet | 列式 | Gzip/Snappy/LZ4 | 极好 | 中等 | 生产推荐:与 Spark 互通 |
| Avro | 行式,带 Schema | Deflate/Snappy | 中等 | 快 | 数据序列化、Kafka 数据落地 |
8.2 ORC 格式详解(推荐)
ORC(Optimized Row Columnar)是 Hive 原生的高性能列式文件格式:
ORC 文件结构:
ORC 文件
├── Stripe 1(默认 64MB)
│ ├── Index Data(轻量级索引,每 10000 行一个)
│ ├── Row Data(实际列数据)
│ └── Stripe Footer(列统计信息)
├── Stripe 2
│ └── ...
└── File Footer
├── 文件级统计信息(行数、列最大/最小值等)
└── Stripe 位置信息
ORC 的核心优化:
- 列投影(Column Projection):只读取查询涉及的列
- 谓词下推(Predicate Pushdown):利用 Stripe/Row Group 统计信息跳过无关数据
- Bloom Filter 索引:快速判断某个 Stripe 是否包含目标值
- Run-Length Encoding(RLE):对整数列压缩效果极好
- Dictionary Encoding:对低基数字符串列压缩率极高
sql
-- 使用 ORC 格式建表
CREATE TABLE sales (
sale_id BIGINT,
product STRING,
amount DECIMAL(12,2),
sale_date DATE
)
STORED AS ORC
TBLPROPERTIES (
'orc.compress'='SNAPPY', -- 压缩算法:NONE/ZLIB/SNAPPY/LZ4(推荐 SNAPPY)
'orc.stripe.size'='67108864', -- Stripe 大小 64MB
'orc.row.index.stride'='10000', -- 行组索引步长
'orc.create.index'='true' -- 启用行组索引
);
-- 将 TextFile 表转换为 ORC
CREATE TABLE sales_orc STORED AS ORC AS SELECT * FROM sales_text;
8.3 Parquet 格式详解
Parquet 是跨语言的通用列式格式,与 Spark、Impala、Presto 互通性最好:
sql
-- 使用 Parquet 建表
CREATE TABLE events_parquet (
event_id STRING,
user_id BIGINT,
event_type STRING,
ts TIMESTAMP
)
STORED AS PARQUET
TBLPROPERTIES (
'parquet.compression'='SNAPPY' -- UNCOMPRESSED/GZIP/SNAPPY/LZO/BROTLI/LZ4
);
8.4 压缩配置
sql
-- 开启中间结果(Map 输出)压缩
SET hive.exec.compress.intermediate=true;
SET mapreduce.map.output.compress=true;
SET mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
-- 开启最终输出压缩
SET hive.exec.compress.output=true;
SET mapreduce.output.fileoutputformat.compress=true;
SET mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
SET mapreduce.output.fileoutputformat.compress.type=BLOCK;
-- Tez 引擎中间数据压缩
SET tez.runtime.compress=true;
SET tez.runtime.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
9. SerDe(Serialization / Deserialization)
SerDe 是 Hive 读写文件的核心接口,决定了 Hive 如何解析和序列化数据。
9.1 常用内置 SerDe
LazySimpleSerDe(默认)
sql
-- 解析分隔符文本
CREATE TABLE csv_data (
id INT,
name STRING,
age INT
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe'
WITH SERDEPROPERTIES (
'field.delim'=',',
'line.delim'='\n',
'null.format'='\\N'
)
STORED AS TEXTFILE;
-- 简写方式(等价于上面)
CREATE TABLE csv_data2 (id INT, name STRING, age INT)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
NULL DEFINED AS '\\N'
STORED AS TEXTFILE;
JsonSerDe
sql
-- 解析 JSON 格式文件(每行一个 JSON)
CREATE TABLE json_logs (
user_id INT,
action STRING,
timestamp BIGINT,
metadata MAP<STRING, STRING>
)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
STORED AS TEXTFILE;
RegexSerDe(正则表达式)
sql
-- 用正则表达式解析复杂格式(如 Apache 日志)
CREATE TABLE apache_logs (
ip STRING,
identity STRING,
user STRING,
time STRING,
request STRING,
status INT,
size INT
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.contrib.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
'input.regex' = '([^ ]*) ([^ ]*) ([^ ]*) \\[([^\\]]*)\\] "([^"]*)" ([0-9]*) ([0-9]*)'
)
STORED AS TEXTFILE;
OpenCSVSerde
sql
-- 处理包含转义字符的复杂 CSV
CREATE TABLE complex_csv (
id STRING,
name STRING,
desc STRING
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde'
WITH SERDEPROPERTIES (
'separatorChar'=',',
'quoteChar'='"',
'escapeChar'='\\'
)
STORED AS TEXTFILE;
10. Hive 内置函数大全
10.1 字符串函数
sql
-- 基本操作
SELECT LENGTH('hello world'); -- 11
SELECT LOWER('Hello WORLD'); -- hello world
SELECT UPPER('hello world'); -- HELLO WORLD
SELECT TRIM(' hello '); -- hello
SELECT LTRIM(' hello '); -- 'hello '
SELECT RTRIM(' hello '); -- ' hello'
-- 截取与拼接
SELECT SUBSTR('hello world', 1, 5); -- hello(下标从 1 开始)
SELECT SUBSTRING('hello world', 7); -- world
SELECT CONCAT('hello', ' ', 'world'); -- hello world
SELECT CONCAT_WS('-', 'a', 'b', 'c'); -- a-b-c(带分隔符)
-- 查找与替换
SELECT INSTR('hello world', 'world'); -- 7(位置)
SELECT LOCATE('world', 'hello world'); -- 7
SELECT REPLACE('hello world', 'world', 'hive'); -- hello hive
SELECT REGEXP_REPLACE('abc123def', '[0-9]+', 'NUM'); -- abcNUMdef
SELECT REGEXP_EXTRACT('abc123def', '[0-9]+', 0); -- 123
-- 分割与拼接
SELECT SPLIT('a,b,c', ','); -- ["a","b","c"](返回数组)
SELECT SPLIT('a,b,c', ',')[1]; -- b(取第2个)
-- 格式化
SELECT LPAD('5', 4, '0'); -- 0005(左填充)
SELECT RPAD('5', 4, '0'); -- 5000(右填充)
SELECT PRINTF('%05d', 42); -- 00042
SELECT FORMAT_NUMBER(1234567.89, 2); -- 1,234,567.89
-- 字符串判断
SELECT LIKE('hello', 'hel%'); -- true
SELECT RLIKE('hello123', '[a-z]+[0-9]+'); -- true
-- 其他
SELECT REVERSE('hello'); -- olleh
SELECT REPEAT('ab', 3); -- ababab
SELECT SPACE(5); -- ' '(5 个空格)
SELECT ASCII('A'); -- 65
SELECT CHR(65); -- A
SELECT QUOTE('it''s'); -- 'it\'s'
SELECT INITCAP('hello world'); -- Hello World
10.2 数学函数
sql
SELECT ROUND(3.567, 2); -- 3.57
SELECT FLOOR(3.7); -- 3
SELECT CEIL(3.2); -- 4
SELECT ABS(-5); -- 5
SELECT SQRT(16); -- 4.0
SELECT POWER(2, 10); -- 1024.0
SELECT LOG(10, 100); -- 2.0(log 以 10 为底)
SELECT LN(2.718281); -- ≈1.0
SELECT EXP(1); -- ≈2.718
SELECT MOD(10, 3); -- 1
SELECT SIGN(-5); -- -1(正数1,负数-1,零0)
SELECT PI(); -- 3.141592653589793
SELECT SIN(0), COS(0), TAN(0); -- 0.0, 1.0, 0.0
SELECT RAND(); -- 0~1 随机数
SELECT RAND(42); -- 固定种子的随机数
SELECT GREATEST(3, 1, 4, 1, 5, 9); -- 9
SELECT LEAST(3, 1, 4, 1, 5, 9); -- 1
SELECT FACTORIAL(5); -- 120
SELECT SHIFTLEFT(8, 2); -- 32(位运算左移)
SELECT SHIFTRIGHT(32, 2); -- 8(位运算右移)
SELECT HEX(255); -- FF
SELECT UNHEX('FF'); -- 255
10.3 日期和时间函数
sql
-- 获取当前时间
SELECT CURRENT_DATE(); -- 2024-01-01
SELECT CURRENT_TIMESTAMP(); -- 2024-01-01 12:00:00.000
SELECT NOW(); -- 等价于 CURRENT_TIMESTAMP()
SELECT UNIX_TIMESTAMP(); -- 当前 Unix 时间戳(秒)
-- 日期解析与格式化
SELECT TO_DATE('2024-01-15 12:30:00'); -- 2024-01-15
SELECT DATE_FORMAT('2024-01-15', 'yyyyMMdd'); -- 20240115
SELECT UNIX_TIMESTAMP('2024-01-01', 'yyyy-MM-dd'); -- → 1704067200
SELECT FROM_UNIXTIME(1704067200); -- 2024-01-01 00:00:00
SELECT FROM_UNIXTIME(1704067200, 'yyyy-MM-dd'); -- 2024-01-01
-- 日期提取
SELECT YEAR('2024-01-15'); -- 2024
SELECT MONTH('2024-01-15'); -- 1
SELECT DAY('2024-01-15'); -- 15
SELECT HOUR('2024-01-15 12:30:45'); -- 12
SELECT MINUTE('2024-01-15 12:30:45'); -- 30
SELECT SECOND('2024-01-15 12:30:45'); -- 45
SELECT WEEKOFYEAR('2024-01-15'); -- 3(第3周)
SELECT DAYOFWEEK('2024-01-15'); -- 2(1=周日,2=周一...)
SELECT QUARTER('2024-04-15'); -- 2
-- 日期运算
SELECT DATE_ADD('2024-01-15', 10); -- 2024-01-25
SELECT DATE_SUB('2024-01-15', 10); -- 2024-01-05
SELECT DATEDIFF('2024-01-15', '2024-01-01'); -- 14(天数差)
SELECT ADD_MONTHS('2024-01-15', 2); -- 2024-03-15
SELECT LAST_DAY('2024-02-01'); -- 2024-02-29(当月最后一天)
SELECT NEXT_DAY('2024-01-15', 'Monday'); -- 下一个星期一
-- 日期截断
SELECT TRUNC('2024-01-15', 'MM'); -- 2024-01-01(月初)
SELECT TRUNC('2024-01-15', 'YY'); -- 2024-01-01(年初)
SELECT DATE_TRUNC('month', '2024-01-15 12:30:00'); -- 2024-01-01 00:00:00
-- 日期比较
SELECT GREATEST('2024-01-15', '2023-12-31'); -- 2024-01-15
10.4 条件函数
sql
-- NVL / COALESCE(空值处理)
SELECT NVL(NULL, 'default'); -- default
SELECT NVL(null_col, 0) FROM table; -- 替换 NULL 为 0
SELECT COALESCE(a, b, c, 'none'); -- 返回第一个非 NULL 值
-- IF
SELECT IF(salary > 10000, '高薪', '低薪') FROM employees;
-- IFF(Hive 2.0+)
SELECT IFF(score >= 60, 'pass', 'fail') FROM students;
-- NULLIF(相等则返回 NULL)
SELECT NULLIF(a, b); -- 若 a=b 则返回 NULL,否则返回 a
-- CASE WHEN(见查询章节)
-- ISNULL / ISNOTNULL
SELECT ISNULL(null_col) FROM table; -- true/false
-- NVL2
SELECT NVL2(email, '有邮箱', '无邮箱') FROM employees;
-- DECODE(Oracle 兼容)
SELECT DECODE(status, 1, '有效', 0, '无效', '未知') FROM orders;
10.5 聚合函数
sql
SELECT
COUNT(*) AS total_rows,
COUNT(email) AS non_null_emails, -- 不统计 NULL
COUNT(DISTINCT dept) AS unique_depts,
SUM(salary) AS total_salary,
AVG(salary) AS avg_salary,
MAX(salary) AS max_salary,
MIN(salary) AS min_salary,
STDDEV_POP(salary) AS std_salary, -- 标准差(总体)
VARIANCE(salary) AS var_salary, -- 方差
COLLECT_SET(dept) AS dept_set, -- 收集唯一值到数组
COLLECT_LIST(dept) AS dept_list, -- 收集所有值到数组(含重复)
PERCENTILE(salary, 0.5) AS median, -- 中位数(数值列)
PERCENTILE_APPROX(salary, 0.5) AS approx_median -- 近似中位数(大数据)
FROM employees;
-- GROUP_CONCAT(类似 MySQL 的 GROUP_CONCAT)
SELECT dept, CONCAT_WS(',', COLLECT_LIST(name)) AS member_names
FROM employees
GROUP BY dept;
-- GROUPING SETS / CUBE / ROLLUP(多维聚合)
-- ROLLUP(层级聚合,等于多个 GROUP BY 结果的 UNION)
SELECT dept, job_level, SUM(salary)
FROM employees
GROUP BY dept, job_level WITH ROLLUP;
-- CUBE(所有维度组合)
SELECT dept, year, SUM(salary)
FROM employees
GROUP BY dept, year WITH CUBE;
-- GROUPING SETS(指定聚合维度)
SELECT dept, country, SUM(salary)
FROM employees
GROUP BY dept, country
GROUPING SETS ((dept, country), (dept), (country), ());
10.6 集合函数
sql
SELECT
SIZE(tags) AS tag_count, -- 数组/Map 的大小
ARRAY_CONTAINS(tags, 'hadoop') AS has_hadoop, -- 是否包含元素
SORT_ARRAY(tags) AS sorted_tags, -- 数组排序
ARRAY_MIN(scores) AS min_score,
ARRAY_MAX(scores) AS max_score,
MAP_KEYS(props) AS prop_keys, -- Map 的键列表
MAP_VALUES(props) AS prop_values, -- Map 的值列表
STR_TO_MAP('a:1,b:2', ',', ':') AS prop_map -- 字符串转 Map
FROM complex_table;
11. 用户自定义函数(UDF / UDAF / UDTF)
11.1 三类自定义函数对比
| 类型 | 全称 | 输入输出关系 | 典型内置示例 | 用途 |
|---|---|---|---|---|
| UDF | User Defined Function | 一行输入 → 一行输出(1:1) | UPPER(), LENGTH() | 字段转换、计算 |
| UDAF | User Defined Aggregate Function | 多行输入 → 一行输出(N:1) | COUNT(), SUM() | 自定义聚合计算 |
| UDTF | User Defined Table-Generating Function | 一行输入 → 多行输出(1:N) | EXPLODE(), JSON_TUPLE() | 行展开、数组拆分 |
11.2 编写简单 UDF
java
// 示例:将字符串中的手机号脱敏(替换中间4位为****)
package com.example.hive.udf;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
@Description(
name = "mask_phone",
value = "_FUNC_(phone) - Masks the middle 4 digits of phone number",
extended = "Example: SELECT mask_phone('13812345678') FROM dual;"
)
public class MaskPhoneUDF extends UDF {
public Text evaluate(Text phone) {
if (phone == null) return null;
String phoneStr = phone.toString();
if (phoneStr.length() != 11) return phone;
return new Text(phoneStr.substring(0, 3) + "****" + phoneStr.substring(7));
}
// 方法重载:处理多种输入类型
public String evaluate(String phone) {
if (phone == null || phone.length() != 11) return phone;
return phone.substring(0, 3) + "****" + phone.substring(7);
}
}
bash
# 编译打包(Maven 方式)
mvn clean package -DskipTests
# 生成 target/my-hive-udf.jar
sql
-- 在 Hive 中注册并使用 UDF
-- 方式1:临时函数(当前会话有效)
ADD JAR hdfs:///jars/my-hive-udf.jar;
CREATE TEMPORARY FUNCTION mask_phone AS 'com.example.hive.udf.MaskPhoneUDF';
-- 方式2:永久函数(写入 Metastore,所有会话可用)
-- 需先将 JAR 上传到 HDFS
CREATE FUNCTION mydb.mask_phone AS 'com.example.hive.udf.MaskPhoneUDF'
USING JAR 'hdfs:///jars/my-hive-udf.jar';
-- 使用自定义函数
SELECT user_id, mask_phone(phone) AS masked_phone FROM users;
-- 查看已注册函数
SHOW FUNCTIONS LIKE 'mask*';
DESCRIBE FUNCTION mask_phone;
DESCRIBE FUNCTION EXTENDED mask_phone;
-- 删除函数
DROP TEMPORARY FUNCTION mask_phone;
DROP FUNCTION IF EXISTS mydb.mask_phone;
11.3 编写 GenericUDF(支持复杂类型)
java
// GenericUDF:支持数组、Map、Struct 等复杂类型
package com.example.hive.udf;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.StringObjectInspector;
@Description(name = "to_upper", value = "_FUNC_(str) - converts string to uppercase")
public class ToUpperGenericUDF extends GenericUDF {
private StringObjectInspector stringOI;
@Override
public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
if (arguments.length != 1) {
throw new UDFArgumentException("to_upper takes exactly one argument");
}
if (!(arguments[0] instanceof StringObjectInspector)) {
throw new UDFArgumentTypeException(0, "to_upper argument must be a string");
}
this.stringOI = (StringObjectInspector) arguments[0];
return PrimitiveObjectInspectorFactory.javaStringObjectInspector; // 返回类型
}
@Override
public Object evaluate(DeferredObject[] arguments) throws HiveException {
String input = stringOI.getPrimitiveJavaObject(arguments[0].get());
if (input == null) return null;
return input.toUpperCase();
}
@Override
public String getDisplayString(String[] children) {
return "to_upper(" + children[0] + ")";
}
}
11.4 编写 UDAF
java
// UDAF:统计字符串列中字符总长度
package com.example.hive.udaf;
import org.apache.hadoop.hive.ql.exec.UDAF;
import org.apache.hadoop.hive.ql.exec.UDAFEvaluator;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
public class TotalLengthUDAF extends UDAF {
public static class TotalLengthEvaluator implements UDAFEvaluator {
private long total = 0;
@Override
public void init() { total = 0; }
// 处理每行输入
public boolean iterate(Text input) {
if (input != null) total += input.getLength();
return true;
}
// 返回部分聚合结果(Map 端输出)
public LongWritable terminatePartial() {
return new LongWritable(total);
}
// 合并部分结果(Reduce 端合并)
public boolean merge(LongWritable partial) {
if (partial != null) total += partial.get();
return true;
}
// 返回最终结果
public LongWritable terminate() {
return new LongWritable(total);
}
}
}
11.5 编写 UDTF
java
// UDTF:将 CSV 字符串拆分为多行
package com.example.hive.udtf;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.*;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import java.util.ArrayList;
public class SplitToRowsUDTF extends GenericUDTF {
private PrimitiveObjectInspector stringOI;
@Override
public StructObjectInspector initialize(ObjectInspector[] argOIs) throws UDFArgumentException {
// 定义输出列
List<String> fieldNames = new ArrayList<>();
List<ObjectInspector> fieldOIs = new ArrayList<>();
fieldNames.add("value");
fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
this.stringOI = (PrimitiveObjectInspector) argOIs[0];
return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
}
@Override
public void process(Object[] args) throws HiveException {
String input = (String) stringOI.getPrimitiveJavaObject(args[0]);
if (input != null) {
String[] parts = input.split(",");
for (String part : parts) {
forward(new Object[]{part.trim()}); // 每个元素输出一行
}
}
}
@Override
public void close() throws HiveException {}
}
sql
-- 使用 UDTF
CREATE TEMPORARY FUNCTION split_to_rows AS 'com.example.hive.udtf.SplitToRowsUDTF'
USING JAR 'hdfs:///jars/my-hive-udf.jar';
-- 直接使用
SELECT split_to_rows(tag_str) AS tag FROM raw_data;
-- 配合 LATERAL VIEW 使用
SELECT id, tag
FROM raw_data
LATERAL VIEW split_to_rows(tag_str) tmp AS tag;
12. Hive 执行引擎
12.1 三种执行引擎对比
| 特性 | MapReduce | Apache Tez | Apache Spark |
|---|---|---|---|
| 计算模型 | Map + Reduce(两阶段) | DAG(任意拓扑) | DAG + 内存 RDD |
| 中间结果 | 写磁盘 HDFS | 写磁盘(可内存缓冲) | 内存(可 spill 磁盘) |
| 延迟 | 高(秒~分钟) | 中(亚秒~秒) | 低(亚秒) |
| 复杂查询 | 多个 MR job,效率低 | 一个 DAG,高效 | 极高效 |
| 容错 | Task 级重试 | 同 MR | RDD 血缘重算 |
| 成熟度 | 最高 | 高(Hive 推荐) | 高 |
| Hive 推荐版本 | Hive 1.x 默认 | Hive 2.x+ 推荐 | Hive on Spark |
12.2 切换执行引擎
sql
-- 查看当前执行引擎
SET hive.execution.engine;
-- 切换到 Tez(推荐,绝大多数生产环境)
SET hive.execution.engine=tez;
-- 切换到 Spark
SET hive.execution.engine=spark;
-- 切换回 MapReduce
SET hive.execution.engine=mr;
12.3 LLAP(Live Long and Process)
LLAP 是 Hive 2.0 引入的交互式查询加速功能:
传统模式:提交查询 → 启动 JVM → 加载数据 → 执行 → 销毁 JVM(每次都有冷启动开销)
LLAP 模式:常驻的 Daemon 进程 → 缓存热数据(行列缓存) → 亚秒级响应
LLAP 核心特性:
- 持久化的 Daemon(避免 JVM 冷启动)
- 行列缓存(将 ORC/Parquet 热数据块缓存在内存)
- 细粒度资源弹性(可根据负载动态调整)
- 支持并发查询
sql
-- 启用 LLAP(需要先部署 LLAP Daemons)
SET hive.llap.execution.mode = auto;
-- 在 hive-site.xml 中配置
-- hive.llap.daemon.memory.per.instance.mb=4096
-- hive.llap.daemon.vcpus.per.instance=4
-- hive.llap.io.enabled=true
13. Hive 性能调优
13.1 小文件合并
Hive 中小文件是最常见的性能问题之一,大量小文件会严重拖慢 NameNode 和 MapReduce 任务:
sql
-- 方式1:查询时合并小文件(Map-Only 任务)
SET hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
SET mapreduce.input.fileinputformat.split.minsize=134217728; -- 128MB
SET mapreduce.input.fileinputformat.split.maxsize=268435456; -- 256MB
-- 方式2:输出时合并小文件
SET hive.merge.mapfiles=true; -- 合并 Map-Only 任务输出
SET hive.merge.mapredfiles=true; -- 合并 MapReduce 任务输出
SET hive.merge.size.per.task=268435456; -- 目标合并文件大小 256MB
SET hive.merge.smallfiles.avgsize=134217728; -- 触发合并的平均文件大小阈值
-- 方式3:定期手动合并(ORC 格式)
ALTER TABLE user_events PARTITION (dt='2024-01-01') CONCATENATE;
-- 方式4:Spark 重写(最彻底的方式)
INSERT OVERWRITE TABLE t PARTITION(dt='2024-01-01')
SELECT * FROM t WHERE dt='2024-01-01';
13.2 分区裁剪优化
sql
-- ❌ 错误:分区条件在函数内,无法裁剪
SELECT * FROM user_events WHERE YEAR(dt) = 2024 AND MONTH(dt) = 1;
-- ✅ 正确:直接在分区列上使用字符串比较
SELECT * FROM user_events WHERE dt BETWEEN '2024-01-01' AND '2024-01-31';
SELECT * FROM user_events WHERE dt >= '2024-01-01' AND dt < '2024-02-01';
-- 验证分区裁剪是否生效
EXPLAIN SELECT * FROM user_events WHERE dt = '2024-01-01';
-- 在执行计划中查找 "partition filter"
-- 强制分区过滤(防止全表扫描)
SET hive.mapred.mode=strict; -- 查询分区表时必须有分区条件,否则报错
13.3 MapJoin(小表 JOIN 优化)
sql
-- 自动 MapJoin(Hive 自动判断小表)
SET hive.auto.convert.join=true; -- 默认 true
SET hive.mapjoin.smalltable.filesize=25000000; -- 小表阈值:25MB
SET hive.auto.convert.join.noconditionaltask=true;
SET hive.auto.convert.join.noconditionaltask.size=10000000; -- 10MB
-- 手动指定 MapJoin(HINT 方式)
SELECT /*+ MAPJOIN(d) */ e.name, d.dept_name
FROM employees e JOIN departments d ON e.dept_id = d.id;
-- Bucket Map Join(两表按相同列分桶,且桶数相同)
SET hive.optimize.bucketmapjoin=true;
SET hive.optimize.bucketmapjoin.sortedmerge=true; -- SMB Join(最高效)
13.4 数据倾斜处理
数据倾斜是导致 Hive 作业长时间卡在 99% 的最常见原因。
sql
-- 1. 开启倾斜 Join 优化
SET hive.optimize.skewjoin=true;
SET hive.skewjoin.key=100000; -- 超过 10 万行认为是热点 key
-- 2. 随机前缀法(解决 GROUP BY 热点 key)
-- 第一步:加随机前缀,拆散热点 key
SELECT CONCAT(FLOOR(RAND() * 100), '_', user_id) AS rkey,
SUM(amount) AS partial_sum
FROM orders GROUP BY CONCAT(FLOOR(RAND() * 100), '_', user_id);
-- 第二步:去掉前缀,再次聚合
SELECT SPLIT(rkey, '_')[1] AS user_id, SUM(partial_sum)
FROM (上面的子查询) t GROUP BY SPLIT(rkey, '_')[1];
-- 3. 过滤 NULL Key(NULL JOIN 时会全部分到同一 Reducer)
SELECT e.*, d.*
FROM employees e
JOIN departments d ON e.dept_id IS NOT NULL AND e.dept_id = d.id;
-- 4. 两阶段聚合(Map 端预聚合)
SET hive.map.aggr=true; -- 开启 Map 端聚合(默认开启)
SET hive.groupby.skewindata=true; -- 倾斜数据随机分发到 Reducer(两次 MR)
13.5 Tez 引擎调优
sql
-- Tez 容器大小(AM 和 Task 都用这个)
SET tez.am.resource.memory.mb=1024;
SET tez.task.resource.memory.mb=2048;
SET tez.task.launch.cmd-opts=-Xmx1800m;
-- 关闭 Tez Session(按需启动,节省资源)
SET tez.session.am.dag.submit.timeout.secs=600;
-- 开启预取优化
SET tez.runtime.enable.final-merge.in.output=true;
-- 增加并行度
SET hive.exec.parallel=true; -- 允许并行执行多个 Stage
SET hive.exec.parallel.thread.number=8; -- 最大并行线程数
13.6 CBO(基于代价的优化器)
sql
-- 启用 CBO(需要先收集统计信息)
SET hive.cbo.enable=true; -- 默认 true(Hive 0.14+)
SET hive.compute.query.using.stats=true;
SET hive.stats.fetch.column.stats=true;
SET hive.stats.fetch.partition.stats=true;
-- 收集统计信息(建议定期对重要表执行)
ANALYZE TABLE employees COMPUTE STATISTICS; -- 表级别统计
ANALYZE TABLE employees COMPUTE STATISTICS FOR COLUMNS; -- 列级别统计
ANALYZE TABLE user_events PARTITION (dt='2024-01-01') COMPUTE STATISTICS;
-- 查看统计信息
DESCRIBE FORMATTED employees; -- 在输出中找 numRows、totalSize
-- 在 hive-site.xml 中启用自动统计收集
-- hive.stats.autogather=true (INSERT/LOAD 后自动更新)
13.7 向量化执行
sql
-- 开启向量化执行(一次处理 1024 行,利用 CPU SIMD 指令)
SET hive.vectorized.execution.enabled=true; -- 默认 true(Hive 0.13+)
SET hive.vectorized.execution.reduce.enabled=true;
SET hive.vectorized.execution.reduce.groupby.enabled=true;
-- 查看向量化是否生效
EXPLAIN VECTORIZATION SELECT * FROM employees;
13.8 常见性能配置汇总
sql
-- 一次性优化配置(可放入 hive-site.xml 或每次 Session 开始时执行)
SET hive.execution.engine=tez;
SET hive.auto.convert.join=true;
SET hive.cbo.enable=true;
SET hive.compute.query.using.stats=true;
SET hive.exec.parallel=true;
SET hive.exec.parallel.thread.number=8;
SET hive.vectorized.execution.enabled=true;
SET hive.exec.dynamic.partition=true;
SET hive.exec.dynamic.partition.mode=nonstrict;
SET hive.merge.mapfiles=true;
SET hive.merge.mapredfiles=true;
SET hive.merge.size.per.task=256000000;
SET hive.map.aggr=true;
SET hive.groupby.skewindata=true;
SET hive.exec.compress.intermediate=true;
SET hive.exec.compress.output=true;
14. Hive 分区管理深入
14.1 静态分区 vs 动态分区
| 对比项 | 静态分区 | 动态分区 |
|---|---|---|
| 分区值 | 手动指定 | 查询时自动从数据中提取 |
| 速度 | 快(无需扫描分区列) | 较慢(需要额外 Reducer) |
| 使用场景 | 已知分区值,如按日期加载 | 分区值多或未知 |
| 语法 | PARTITION (dt='2024-01-01') |
PARTITION (dt) |
sql
-- 静态分区
INSERT INTO TABLE user_events PARTITION (dt='2024-01-01', country='CN')
SELECT user_id, event_type, event_time, page_url
FROM raw_events_20240101;
-- 动态分区(分区列放在 SELECT 最后)
SET hive.exec.dynamic.partition=true;
SET hive.exec.dynamic.partition.mode=nonstrict;
SET hive.exec.max.dynamic.partitions=1000; -- 最大动态分区数
SET hive.exec.max.dynamic.partitions.pernode=300; -- 每节点最大动态分区数
INSERT OVERWRITE TABLE user_events PARTITION (dt, country)
SELECT user_id, event_type, event_time, page_url, dt, country -- dt 和 country 在最后
FROM raw_events;
14.2 分区命名规范建议
sql
-- 推荐:字符串分区键,格式一致
PARTITIONED BY (dt STRING COMMENT 'date partition, format: yyyyMMdd')
-- 推荐:多级分区,粗粒度在前
PARTITIONED BY (year STRING, month STRING, day STRING)
-- 注意:分区值本身不存储在数据文件中,只体现在目录名中
-- 不建议将高基数列(如 user_id)作为分区键,会产生大量小文件
14.3 分区维护
sql
-- 检查并修复分区(HDFS 目录存在但 Metastore 无记录)
MSCK REPAIR TABLE user_events;
-- 检查分区完整性
SHOW PARTITIONS user_events;
-- 查看特定分区的详情
DESCRIBE FORMATTED user_events PARTITION (dt='2024-01-01');
-- 交换分区(在两张相同结构的表间快速移动分区数据)
ALTER TABLE user_events EXCHANGE PARTITION (dt='2024-01-01') WITH TABLE user_events_staging;
-- 截断分区(仅清空数据,保留分区元数据)
TRUNCATE TABLE user_events PARTITION (dt='2023-01-01');
-- 归档分区(压缩为 Hadoop Archive 文件,减少小文件数量)
SET hive.archive.enabled=true;
ALTER TABLE user_events ARCHIVE PARTITION (dt='2023-01-01');
ALTER TABLE user_events UNARCHIVE PARTITION (dt='2023-01-01');
15. Hive 元数据服务(Metastore)
15.1 Metastore 三种部署模式
嵌入式模式(Embedded)
Hive CLI ─── Driver ─── Metastore API ─── Derby(嵌入式数据库)
- Derby 数据库随 Hive 进程一起启动
- 只支持单连接
- 仅适合学习和测试
本地模式(Local)
Hive CLI ─── Driver ─── Metastore API ─── MySQL(同机)
- Metastore 在 Hive 进程内运行,但使用外部 MySQL
- 支持多连接,但每个 Hive 进程都直接连接数据库
- 适合小型集群
远程模式(Remote,生产推荐)
Hive Client 1 ─┐
Hive Client 2 ─┼─── Thrift Server ─── Metastore API ─── MySQL
Spark/Impala ──┘ (9083 端口)
- Metastore 作为独立服务运行
- 所有客户端通过 Thrift 协议连接
- 支持多客户端并发
- 数据库连接由 Metastore 服务统一管理
15.2 Metastore 重要配置
xml
<!-- 元数据服务地址 -->
<property>
<name>hive.metastore.uris</name>
<value>thrift://metastore-host:9083</value>
</property>
<!-- 元数据库连接池配置 -->
<property>
<name>datanucleus.connectionPoolingType</name>
<value>DBCP</value>
</property>
<property>
<name>datanucleus.connectionPool.maxPoolSize</name>
<value>20</value>
</property>
<!-- 开启元数据缓存(减少 DB 查询) -->
<property>
<name>hive.metastore.cache.pinobjtypes</name>
<value>Table,StorageDescriptor,SerDeInfo,Partition,Database,Type,FieldSchema,Order</value>
</property>
<!-- Schema 版本兼容性检查 -->
<property>
<name>hive.metastore.schema.verification</name>
<value>true</value>
</property>
15.3 Metastore HA(高可用)
xml
<!-- 配置多个 Metastore 实例实现高可用 -->
<property>
<name>hive.metastore.uris</name>
<value>thrift://metastore1:9083,thrift://metastore2:9083</value>
</property>
在多个节点上分别启动 Metastore 服务:
bash
hive --service metastore & # 在 metastore1 上
hive --service metastore & # 在 metastore2 上
16. HiveServer2 与 Beeline
16.1 HiveServer2(HS2)
HiveServer2 是 Hive 提供远程多并发客户端访问的服务:
核心特性:
- 多并发:支持多个客户端同时连接(HiveServer1 不支持)
- 认证:Kerberos、LDAP、自定义认证
- JDBC/ODBC 接口:标准 JDBC(端口 10000)
- Web UI:默认 10002 端口,可监控 Sessions 和 Queries
bash
# 启动 HiveServer2
hive --service hiveserver2 &
# 或者(推荐,重定向日志)
nohup $HIVE_HOME/bin/hiveserver2 > /var/log/hive/hiveserver2.log 2>&1 &
# 查看 Web UI
http://localhost:10002
16.2 Beeline 使用详解
Beeline 是基于 JDBC 的 HiveServer2 客户端,推荐替代旧的 Hive CLI:
bash
# 连接方式1:本地连接(不需要认证)
beeline -u jdbc:hive2://localhost:10000
# 连接方式2:指定用户名密码
beeline -u jdbc:hive2://localhost:10000 -n hive -p password
# 连接方式3:Kerberos 认证
beeline -u "jdbc:hive2://localhost:10000/default;principal=hive/hostname@REALM"
# 连接方式4:指定数据库
beeline -u jdbc:hive2://localhost:10000/mydb
# 非交互式:执行单个查询后退出
beeline -u jdbc:hive2://localhost:10000 -e "SELECT COUNT(*) FROM employees"
# 非交互式:执行 SQL 脚本文件
beeline -u jdbc:hive2://localhost:10000 -f /path/to/script.hql
# 关闭 beeline header 和 footer(适合脚本解析)
beeline --silent=true --outputformat=tsv2 -u jdbc:hive2://localhost:10000 \
-e "SELECT * FROM employees" > output.tsv
Beeline 交互式命令:
sql
-- 连接到 HiveServer2
!connect jdbc:hive2://localhost:10000
-- 断开连接
!disconnect
-- 退出
!quit
-- 查看所有命令
!help
-- 查看当前连接信息
!info
-- 执行 Shell 命令
!sh ls /data/
-- 查看历史命令
!history
-- 设置输出格式
!set outputformat table -- 表格(默认)
!set outputformat csv2 -- CSV
!set outputformat tsv2 -- TSV
!set outputformat json -- JSON
-- 显示/隐藏列 Header
!set headerInterval 0 -- 只显示一次 Header
!set showHeader true/false
-- 设置最大行宽
!set maxWidth 200
!set maxColumnWidth 50
16.3 JDBC 程序连接 Hive
java
import java.sql.*;
public class HiveJDBCExample {
private static final String JDBC_URL = "jdbc:hive2://localhost:10000/default";
private static final String DRIVER_CLASS = "org.apache.hive.jdbc.HiveDriver";
public static void main(String[] args) throws Exception {
Class.forName(DRIVER_CLASS);
try (Connection conn = DriverManager.getConnection(JDBC_URL, "hive", "");
Statement stmt = conn.createStatement()) {
// 创建表
stmt.execute("CREATE TABLE IF NOT EXISTS test_jdbc (id INT, name STRING) STORED AS ORC");
// 插入数据
stmt.execute("INSERT INTO test_jdbc VALUES (1, 'Alice'), (2, 'Bob')");
// 查询
try (ResultSet rs = stmt.executeQuery("SELECT * FROM test_jdbc")) {
ResultSetMetaData metaData = rs.getMetaData();
while (rs.next()) {
for (int i = 1; i <= metaData.getColumnCount(); i++) {
System.out.print(rs.getString(i) + "\t");
}
System.out.println();
}
}
}
}
}
Maven 依赖:
xml
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-jdbc</artifactId>
<version>3.1.3</version>
</dependency>
17. Hive 安全机制
17.1 认证方式
| 认证方式 | 适用场景 | 配置方式 |
|---|---|---|
| 无认证 | 开发/测试 | 默认 |
| LDAP | 企业 Active Directory 集成 | hive.server2.authentication=LDAP |
| Kerberos | 企业生产安全环境 | hive.server2.authentication=KERBEROS |
| Custom | 自定义认证插件 | hive.server2.authentication=CUSTOM |
| PAM | Linux PAM 认证 | hive.server2.authentication=PAM |
xml
<!-- Kerberos 认证配置示例 -->
<property>
<name>hive.server2.authentication</name>
<value>KERBEROS</value>
</property>
<property>
<name>hive.server2.kerberos.principal</name>
<value>hive/_HOST@YOUR-REALM.COM</value>
</property>
<property>
<name>hive.server2.kerberos.keytab</name>
<value>/etc/security/keytabs/hive.service.keytab</value>
</property>
17.2 授权机制
sql
-- Hive 内置权限控制(基于 SQL 标准)
-- 启用(hive-site.xml 中):
-- hive.security.authorization.enabled=true
-- hive.security.authorization.manager=org.apache.hadoop.hive.ql.security.authorization.plugin.sqlstd.SQLStdHiveAuthorizerFactory
-- 创建角色
CREATE ROLE analyst;
CREATE ROLE data_engineer;
-- 授予角色权限
GRANT SELECT ON DATABASE sales_db TO ROLE analyst;
GRANT ALL ON TABLE employees TO ROLE data_engineer;
GRANT SELECT ON TABLE employees (name, dept) TO ROLE analyst; -- 列级权限
-- 将角色赋予用户
GRANT ROLE analyst TO USER alice;
GRANT ROLE data_engineer TO USER bob;
-- 撤销权限
REVOKE SELECT ON DATABASE sales_db FROM ROLE analyst;
REVOKE ROLE analyst FROM USER alice;
-- 查看权限
SHOW GRANT USER alice ON TABLE employees;
SHOW ROLE GRANT USER alice;
SHOW GRANTS ON TABLE employees;
-- 删除角色
DROP ROLE analyst;
17.3 与 Apache Ranger 集成
Apache Ranger 提供企业级细粒度权限控制:
用户请求 → HiveServer2 → Ranger 插件鉴权 → 允许/拒绝
↑
Ranger Admin(策略管理中心)
↑
Ranger Audit(审计日志)
Ranger 支持:
- 数据库/表/列/行级别权限控制
- 基于用户/组/角色的权限策略
- 数据掩码(敏感数据自动脱敏)
- 行过滤(不同用户看到不同行)
- 完整的审计日志
18. Hive 与生态系统集成
18.1 Hive on Spark
bash
# 在 hive-site.xml 中配置
# hive.execution.engine=spark
# spark.master=yarn
# spark.submit.deployMode=cluster
sql
-- 切换到 Spark 引擎
SET hive.execution.engine=spark;
-- Spark 相关配置
SET spark.executor.memory=4g;
SET spark.executor.cores=2;
SET spark.executor.instances=10;
SET spark.dynamicAllocation.enabled=true;
-- 执行查询(透明使用 Spark 计算)
SELECT dept, AVG(salary) FROM employees GROUP BY dept;
18.2 与 Spark SQL 共享 Metastore
Spark SQL 和 Hive 可以共享同一个 Metastore,让两个系统看到相同的表结构:
scala
// Spark 代码中配置 Hive Metastore
val spark = SparkSession.builder()
.appName("HiveIntegration")
.config("spark.sql.warehouse.dir", "/user/hive/warehouse")
.config("hive.metastore.uris", "thrift://localhost:9083")
.enableHiveSupport()
.getOrCreate()
// 直接查询 Hive 表
spark.sql("SELECT * FROM sales_db.orders LIMIT 10").show()
// Spark 写入的表在 Hive 中可见
spark.sql("""
CREATE TABLE IF NOT EXISTS spark_result
USING ORC
AS SELECT dept, SUM(salary) FROM employees GROUP BY dept
""")
18.3 与 HBase 集成
sql
-- 创建 Hive-HBase 映射表(通过 HBase StorageHandler)
CREATE TABLE hbase_users (
key STRING,
name STRING,
age INT,
email STRING
)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
'hbase.columns.mapping' = ':key,info:name,info:age,info:email'
)
TBLPROPERTIES ('hbase.table.name' = 'users');
-- 通过 Hive 查询 HBase 数据
SELECT * FROM hbase_users WHERE key = 'user001';
18.4 与 Apache Iceberg 集成(Hive 4.x)
Apache Iceberg 是新一代开放表格式,Hive 4.x 对其提供原生支持:
sql
-- 创建 Iceberg 表
CREATE TABLE iceberg_orders (
order_id BIGINT,
user_id INT,
amount DECIMAL(10,2),
status STRING,
create_ts TIMESTAMP
)
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler'
LOCATION '/warehouse/iceberg/orders'
TBLPROPERTIES (
'table_type'='ICEBERG',
'format-version'='2', -- Iceberg v2(支持 Row-level Updates)
'write.delete.mode'='merge-on-read' -- 删除模式
);
-- Iceberg 支持的时间旅行(Time Travel)
-- 查询历史快照
SELECT * FROM iceberg_orders FOR SYSTEM_TIME AS OF '2024-01-01 00:00:00';
SELECT * FROM iceberg_orders FOR SYSTEM_VERSION AS OF 12345678;
-- 查看表历史
SELECT * FROM iceberg_orders.history;
SELECT * FROM iceberg_orders.snapshots;
-- Iceberg Z-order 排序(Hive 4.x 新特性)
ALTER TABLE iceberg_orders EXECUTE OPTIMIZE(ZORDER BY (user_id, create_ts));
18.5 与 Sqoop 集成
bash
# Sqoop 从 MySQL 导入数据到 Hive 表
sqoop import \
--connect jdbc:mysql://mysql-host:3306/sales \
--username root --password secret \
--table orders \
--hive-import \
--hive-database sales_db \
--hive-table orders_from_mysql \
--hive-overwrite \
--num-mappers 4
# Sqoop 将 Hive 表数据导出到 MySQL
sqoop export \
--connect jdbc:mysql://mysql-host:3306/sales \
--username root --password secret \
--table mysql_target \
--hcatalog-database sales_db \
--hcatalog-table hive_source
19. Hive 4.x 新特性
19.1 Iceberg v3 深度集成
sql
-- 支持 Iceberg v3 功能:Deletion Vectors(删除向量)
-- 比 MoR 模式更高效的行级删除实现
CREATE TABLE orders_v3 (...)
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler'
TBLPROPERTIES (
'format-version'='3',
'write.delete.mode'='merge-on-read'
);
-- Z-ordering 优化(空间近邻填充曲线,提升多列过滤性能)
ALTER TABLE orders_v3 EXECUTE OPTIMIZE(ZORDER BY (user_id, date));
-- Variant 类型(半结构化数据,类似 JSON Schema)
CREATE TABLE semi_struct (id BIGINT, data VARIANT);
19.2 JDK 21 支持
Hive 4.x 支持 Java 21,可使用虚拟线程(Virtual Threads)提升 HS2 并发性能。
19.3 HMS REST Catalog
sql
-- Hive Metastore 暴露 REST API,兼容 Apache Iceberg REST Catalog 协议
-- 允许 Spark、Flink 通过标准 REST API 访问 Hive 元数据
19.4 内置自动 Compaction
Hive 4.x 加入了 ACID 表的自动 Compaction 功能,无需手动触发:
sql
-- 查看 compaction 队列
SHOW COMPACTIONS;
-- 手动触发 compaction(Major:合并所有增量到基础文件)
ALTER TABLE student COMPACT 'major';
-- 手动触发 Minor compaction(合并增量文件)
ALTER TABLE student COMPACT 'minor';
20. 常见问题与排查
20.1 Hive 查询卡住不动
排查步骤:
bash
# 1. 查看 YARN 作业状态
yarn application -list
yarn application -status <application_id>
# 2. 查看 Hive 日志
tail -f $HIVE_HOME/logs/hive.log
# 3. 查看 YARN 作业日志
yarn logs -applicationId application_XXXXX
# 4. 检查是否有 Reducer 数据倾斜(YARN UI http://master:8088 中查看各 Reducer 进度)
20.2 "Too many dynamic partitions" 错误
sql
-- 错误信息:Number of dynamic partitions created is 2000, which is more than 1000
-- 解决:增大动态分区限制
SET hive.exec.max.dynamic.partitions=5000;
SET hive.exec.max.dynamic.partitions.pernode=1000;
20.3 Out of Memory 错误
sql
-- 增大 Map/Reduce 内存
SET mapreduce.map.memory.mb=4096;
SET mapreduce.reduce.memory.mb=8192;
SET mapreduce.map.java.opts=-Xmx3276m;
SET mapreduce.reduce.java.opts=-Xmx6553m;
-- 增大 Tez 容器大小
SET tez.task.resource.memory.mb=4096;
SET tez.task.launch.cmd-opts=-Xmx3276m;
20.4 Metastore 连接失败
bash
# 检查 Metastore 服务是否运行
netstat -tuln | grep 9083
# 或
ps aux | grep HiveMetaStore
# 重启 Metastore
pkill -f HiveMetaStore
nohup hive --service metastore > /tmp/metastore.log 2>&1 &
# 检查 MySQL 连接
mysql -h localhost -u hive -p metastore
# 检查 schema 版本
schematool -dbType mysql -info
20.5 "Unable to move source" 错误
bash
# 原因:HDFS 临时目录权限问题
hdfs dfs -chmod 777 /tmp/hive
hdfs dfs -chown -R hive:hdfs /user/hive/warehouse
# 在 hive-site.xml 中设置 hive 临时目录
# hive.exec.scratchdir=/tmp/hive
# hive.downloaded.resources.dir=/tmp/hive/resources
20.6 全表扫描警告
sql
-- 错误:Strict mode requires that a partition filter is present before returning from this call
-- 原因:hive.mapred.mode=strict 时,查询分区表必须有分区条件
-- 解决方案1:添加分区条件
SELECT * FROM user_events WHERE dt = '2024-01-01';
-- 解决方案2:临时关闭 strict 模式
SET hive.mapred.mode=nonstrict;
20.7 Join 内存溢出(小表 MapJoin 失败)
sql
-- 增大 MapJoin 阈值
SET hive.mapjoin.smalltable.filesize=128000000; -- 128MB
SET hive.auto.convert.join.noconditionaltask.size=128000000;
-- 或关闭自动 MapJoin,改用普通 Join
SET hive.auto.convert.join=false;
21. 最佳实践总结
21.1 建表规范
sql
-- ✅ 推荐建表模式
CREATE TABLE sales_order (
order_id BIGINT COMMENT '订单ID(主键)',
user_id INT COMMENT '用户ID',
product_id INT COMMENT '商品ID',
amount DECIMAL(12,2) COMMENT '订单金额',
create_ts TIMESTAMP COMMENT '下单时间'
)
COMMENT '销售订单明细表'
PARTITIONED BY (dt STRING COMMENT '日期分区 yyyyMMdd')
STORED AS ORC
TBLPROPERTIES (
'orc.compress'='SNAPPY',
'author'='data_eng_team',
'create_time'='2024-01-01'
);
21.2 数据导入规范
bash
# 1. 批量数据:使用 LOAD DATA 或 INSERT OVERWRITE
# 2. 增量数据:INSERT INTO(追加)
# 3. 大批量 ETL:INSERT OVERWRITE TABLE ... SELECT ...(效率最高)
# 4. 导入后运行 ANALYZE TABLE 收集统计信息
21.3 查询优化清单
□ 分区表查询必须包含分区条件
□ 优先使用 ORC 或 Parquet 格式
□ 小表 JOIN 使用 MAPJOIN hint
□ 避免 SELECT *,只查需要的列
□ GROUP BY 使用高基数列(低基数考虑分桶后 SMB Join)
□ 使用 EXPLAIN 分析执行计划
□ 对核心表执行 ANALYZE TABLE 收集统计信息
□ 避免在 WHERE 条件中对分区列使用函数
□ 大查询优先使用 Tez 引擎
□ 数据倾斜时使用 hive.groupby.skewindata=true
21.4 EXPLAIN 使用
sql
-- 查看执行计划
EXPLAIN SELECT dept, COUNT(*) FROM employees WHERE salary > 10000 GROUP BY dept;
-- 查看详细执行计划(含操作符树)
EXPLAIN EXTENDED SELECT ...;
-- 查看依赖关系
EXPLAIN DEPENDENCY SELECT ...;
-- 查看向量化信息
EXPLAIN VECTORIZATION SELECT ...;
-- 查看 CBO 相关信息(需 SET hive.cbo.enable=true)
EXPLAIN ANALYZE SELECT ...;
21.5 Hive 命令行技巧
bash
# 执行单个 HQL 后退出
hive -e "SELECT COUNT(*) FROM employees"
# 执行 HQL 脚本文件
hive -f /data/scripts/report.hql
# 传递变量到 HQL 脚本(使用 hiveconf)
hive -f report.hql --hiveconf dt=2024-01-01
# 在 HQL 中使用:WHERE dt = '${hiveconf:dt}'
# 静默模式(去掉 INFO 日志)
hive -S -e "SELECT ..."
# 初始化 HQL(每次启动时执行)
hive --hiveconf hive.session.id=mysession -i /data/scripts/init.hql -f report.hql
22. Hive 与其他技术对比
22.1 Hive vs Spark SQL
| 对比维度 | Hive(+Tez) | Spark SQL |
|---|---|---|
| 擅长场景 | 超大批量 ETL,PB 级离线处理 | 复杂分析,迭代计算,ML 管道 |
| 查询延迟 | 秒~分钟级 | 亚秒~秒级 |
| 内存使用 | 磁盘为主,内存需求低 | 内存密集型 |
| SQL 兼容 | HiveQL(HQL),自己的方言 | ANSI SQL,兼容 HiveQL |
| 生态整合 | Hadoop 原生,Metastore 标准 | 支持 Hive Metastore |
| 小文件处理 | 需要手动合并 | 自动处理能力强 |
| 流批一体 | 仅批处理 | 批处理 + Structured Streaming |
22.2 Hive vs Presto/Trino
| 对比维度 | Hive | Presto/Trino |
|---|---|---|
| 定位 | 批处理 ETL 工具 | 交互式 OLAP 查询引擎 |
| 延迟 | 高(秒~分钟) | 低(毫秒~秒,MPP 并行) |
| 数据源 | HDFS/S3/HBase | 多源联邦:Hive/MySQL/S3/Kafka 等 |
| 大数据集 | 擅长(PB 级 ETL) | 内存有限,超大结果集可能 OOM |
| SQL 支持 | HiveQL 扩展 | 接近标准 ANSI SQL |
| 使用场景 | 数据入库、转换、批量报表 | BI 即席查询、实时大屏 |
22.3 Hive vs Impala
| 对比维度 | Hive | Impala |
|---|---|---|
| 架构 | MapReduce/Tez/Spark 执行 | 原生 MPP,C++ 实现 |
| 延迟 | 高 | 低(内存 MPP,毫秒级) |
| 容错 | 强(MR/Tez 容错机制) | 弱(查询失败需重试) |
| UDF | Java/Python | Java(有限) |
| 共享 Metastore | 是(HMS) | 是(共享 Hive HMS) |
| 适用场景 | 大批量 ETL | 快速分析查询 |
22.4 2024 年 Hive 的定位
Hive 在现代数据栈中的核心价值仍然存在:
数据湖架构中的 Hive 角色(2024):
存储层: HDFS / S3 / OSS(对象存储)
表格式: Hive 表 / Apache Iceberg / Apache Hudi / Apache Delta Lake
批处理: Hive(ETL)/ Spark(复杂计算)
即席查询:Presto / Trino / Spark SQL
元数据: Hive Metastore(HMS,事实上的行业标准)
HMS(Hive Metastore Service)已成为大数据生态的元数据中枢,即使不使用 Hive 做查询,Spark、Presto、Flink 等都依赖 HMS 管理表元数据。这是 Hive 在现代架构中最核心的价值所在。
23. 附录:配置项速查 & 官方资源
23.1 重要配置项汇总
| 配置项 | 默认值 | 说明 |
|---|---|---|
hive.execution.engine |
mr |
执行引擎:mr/tez/spark |
hive.metastore.warehouse.dir |
/user/hive/warehouse |
Hive 仓库目录 |
hive.exec.dynamic.partition |
false |
是否启用动态分区 |
hive.exec.dynamic.partition.mode |
strict |
动态分区模式:strict/nonstrict |
hive.exec.max.dynamic.partitions |
1000 |
最大动态分区数 |
hive.auto.convert.join |
true |
自动转换 MapJoin |
hive.mapjoin.smalltable.filesize |
25MB |
MapJoin 小表阈值 |
hive.cbo.enable |
true |
启用 CBO 优化器 |
hive.vectorized.execution.enabled |
true |
启用向量化执行 |
hive.exec.parallel |
false |
并行执行多个 Stage |
hive.merge.mapfiles |
true |
合并 Map-only 输出小文件 |
hive.merge.mapredfiles |
false |
合并 MR 输出小文件 |
hive.groupby.skewindata |
false |
数据倾斜时随机分发 |
hive.map.aggr |
true |
Map 端预聚合 |
hive.mapred.mode |
nonstrict |
strict 模式强制分区条件 |
hive.optimize.skewjoin |
false |
倾斜 Join 优化 |
hive.server2.thrift.port |
10000 |
HiveServer2 端口 |
hive.metastore.port |
9083 |
Metastore Thrift 端口 |
hive.server2.webui.port |
10002 |
HiveServer2 Web UI 端口 |
23.2 默认端口速查
| 服务 | 端口 | 协议 |
|---|---|---|
| Hive Metastore | 9083 | Thrift |
| HiveServer2(JDBC/ODBC) | 10000 | Thrift/HTTP |
| HiveServer2 Web UI | 10002 | HTTP |
| Hive CLI(本地) | - | 本地进程 |
23.3 官方资源链接
23.4 常用 SQL 速查
sql
-- 查看当前配置
SET hive.execution.engine; -- 查看单个配置
SET; -- 查看所有配置
-- 查看当前数据库
SELECT current_database();
-- 查看 Hive 版本
SELECT version();
-- 查看函数列表
SHOW FUNCTIONS;
SHOW FUNCTIONS LIKE 'str*';
-- 查看建表语句
SHOW CREATE TABLE table_name;
-- 查看分区列表
SHOW PARTITIONS table_name;
-- 查看锁
SHOW LOCKS table_name;
-- 释放锁
UNLOCK TABLE table_name;
-- 查看正在运行的查询(HS2 Web UI)
-- http://localhost:10002/queries
-- 执行计划
EXPLAIN [EXTENDED|VECTORIZATION|DEPENDENCY|AUTHORIZATION|ANALYZE] query;