Hive概述
Hive核心是将HQL转换为MapReduce程序,然后将程序提交到Hadoop群集执行。Hive的最大的魅力在于用户专注于编写HQL,Hive帮您转换成为MapReduce程序完成对数据的分析。
Hive基础架构
执行流程
Metastore (元数据服务) —— 图书馆的目录卡片
MySQL 原理: 数据文件(.ibd)里既有数据又有表结构定义。
Hive 原理: 数据只是 HDFS 上的一堆文本文件(CSV, JSON, Parquet)。Hive 怎么知道哪一列是
user_id?全靠 Metastore。它通常是一个独立的 MySQL 数据库,记录了:
表A -> 对应 HDFS 路径 /data/table_a,以及第一列是 int, 第二列是 string。
实战影响: 如果你删了 HDFS 文件,表还在(读的时候报错);如果你删了 Metastore 里的元数据,文件还在(变成了无主孤魂的垃圾文件)。
Drver (驱动器) —— SQL 编译器
这是 Hive 的核心。它负责把你写的 HQL 解析成抽象语法树 (AST),然后优化,最后生成 执行计划 (Execution Plan)。
实战影响: 你需要学会看
EXPLAIN命令,看看 Driver 把你的 SQL 翻译成了几个 MapReduce 任务。
3. 执行引擎 (MapReduce / Tez / Spark) —— 真正的苦力
MySQL 自己有执行线程。
Hive 没有自己的计算能力,它要把任务外包给计算引擎(默认是 MapReduce,现在主流是 Spark 或 Tez)。
实战影响: 一个简单的
GROUP BY操作,在底层会引发巨大的网络传输(Shuffle)。理解了这个,你就知道为什么要避免数据倾斜了。




Hive metadata
因为HDFS里存储的一般是JSON或者CSV格式的,不想MySQL数据库那样有结构,那么Hive SQL该怎么找到对应的列呢?通过metaddata
Metastore 后端通常也是一个 MySQL 数据库。 如果你连上这个 MySQL,你会看到这几张核心表(不要在生产环境手动改它们!):
TBLS: 存储表的名称、Owner、类型(Managed/External)、对应SDS表的 ID。DBS: 存储数据库的信息(对应 Hive 的 Database)。SDS(Storage Descriptors): 最核心的表。存储了 HDFS 路径 (LOCATION)、输入输出格式类名、是否压缩等。COLUMNS_V2: 存储具体的列名、类型、顺序。SERDES&SERDE_PARAMS: 存储具体的分隔符(比如field.delim是,)。
当你执行 SELECT name FROM users 时,Driver 访问 Metastore 的对话其实是这样的:
Driver: "我要查
users表。"Metastore (查询
TBLS): "有这张表,属于default库。"Driver: "它的
name字段在哪?"Metastore (查询
COLUMNS_V2): "是第 2 列,类型是 String。"Driver: "文件在哪?怎么解析?"
Metastore (查询
SDS和SERDE_PARAMS):"文件在
hdfs://.../users。""格式是
TextFile。""分隔符是逗号
,(ASCII 44)。"
Driver: "收到。我生成一个任务:去那个路径读文件,按逗号切分,取第 2 块数据作为
name返回。"
Hive SQL
SQL 是 Structured Query Language 的缩写,中文叫 结构化查询语言。
DML 是 Data Manipulation Language 的缩写,中文叫 数据操作语言。 它主要用于对数据库中已有的表进行数据增删改查,也就是对表里的记录(行)进行操作,而不是改表结构。
DDL 是 Data Definition Language 的缩写,中文一般叫 数据定义语言。 它是一类 用来定义和管理数据库结构 的 SQL 语句,主要负责“建库建表”这类操作,而不是操作表里的具体数据。
太棒了!既然你已经打通了任督二脉(架构原理),那我们现在正式开始练招(Hive SQL)。
作为后端开发,学习 Hive SQL 最痛苦的不是“学不会”,而是“习惯性动作导致的坑”。
我们将按照 DDL (建表) -> DML (数据导入) -> DQL (查询) 的顺序,每一个环节都把你熟悉的 MySQL 和 Hive 拉出来“单挑”,让你一眼看穿区别。
1. DDL 篇
这是差异最大的地方。MySQL 建表关注索引和引擎,Hive 建表关注数据的物理格式。
场景:建立 users 表
假设数据还是那个 CSV:id, name, age, city,逗号分隔。
代码对比
MySQL 写法:
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
age INT,
city VARCHAR(50)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;Hive 写法 (标准生产环境版):
CREATE EXTERNAL TABLE users ( -- 1. 推荐使用 EXTERNAL (外部表)
id INT COMMENT '用户ID',
name STRING COMMENT '姓名', -- 2. 类型直接用 STRING
age INT COMMENT '年龄',
city STRING COMMENT '城市'
)
COMMENT '用户基础信息表'
PARTITIONED BY (dt STRING) -- 3. 核心:按日期分区 (MySQL很少这么写)
ROW FORMAT DELIMITED -- 4. 告诉 SerDe 怎么解析
FIELDS TERMINATED BY ',' -- 列分隔符是逗号
STORED AS TEXTFILE -- 5. 存储格式 (生产环境常用 ORC/Parquet)
LOCATION '/data/warehouse/users'; -- 6. 指定 HDFS 路径 (可选)🛑 避坑指南:
EXTERNAL: 后端习惯DROP TABLE删库跑路。在 Hive 里,如果是EXTERNAL表,DROP只删元数据,HDFS 数据还在。这在生产环境是保命符。PARTITIONED BY: 看到dt了吗?它不在(id, name...)的定义里,但在 HDFS 上会变成/dt=2023-11-22/这样的文件夹。这是 Hive 唯一的“粗粒度索引”。
2. DML 篇
后端开发习惯用 INSERT 一条条插数据,这在 Hive 里是绝对禁止的。
代码对比
MySQL 写法:
INSERT INTO users (name, age, city) VALUES ('zhangsan', 18, 'beijing');
-- 只有少量数据这样做Hive 写法 1:搬运文件 (最快)
-- 假设你已经把 100GB 的 log.csv 上传到了 HDFS 的 /tmp 目录
-- 这句话瞬间完成,因为只是改了 HDFS 的文件路径元数据
LOAD DATA INPATH '/tmp/log.csv'
INTO TABLE users
PARTITION (dt='2023-11-22');Hive 写法 2:清洗数据 (常用)
-- 从临时表 A 选出数据,经过处理,覆盖写入正式表 B
INSERT OVERWRITE TABLE users PARTITION (dt='2023-11-22')
SELECT id, name, age, city
FROM tmp_users
WHERE dt = '2023-11-22' AND age > 0;🛑 避坑指南:
不要用
INSERT INTO ... VALUES ...:虽然 Hive 支持,但每插入一条都会生成一个小文件。几万个小文件会把 NameNode (HDFS 的主节点) 搞挂。OVERWRITE: Hive 数仓为了保证幂等性,通常用INSERT OVERWRITE。如果任务重跑,它会先清空该分区,再写入,防止数据重复。
3. DQL 篇
语法 90% 相似,但执行代价天差地别。
代码对比:去重统计
场景: 统计有多少个不同的用户。
MySQL 写法:
SELECT COUNT(DISTINCT user_id) FROM users;Hive 写法 (优化版):
如果数据量过亿,上面的写法在 Hive 里可能会跑几个小时甚至报错。
后端思维转换: 用“多线程分组”代替“单点去重”。
-- 1. 错误/低效写法 (数据倾斜风险)
SELECT COUNT(DISTINCT user_id) FROM users WHERE dt='2023-11-22';
-- 2. 高效写法 (MapReduce 思维)
-- 先 Group By 把数据打散到不同的 Reducer,再 Count
SELECT COUNT(1)
FROM (
SELECT user_id
FROM users
WHERE dt='2023-11-22'
GROUP BY user_id -- 这里利用了多节点并行去重
) t;避坑指南
分区过滤是强制的:在很多公司,如果你的 SQL
WHERE里没有dt=...,SQL 审核平台直接会拦截你的查询,不允许提交。别指望秒级响应:在 MySQL 里你可能习惯不断试错修改 SQL;在 Hive 里,跑一次 SQL 可能要 10 分钟,所以写之前要在脑子里(或者用
EXPLAIN)先预演一遍。
总结
建表 (Create): 记得加
EXTERNAL,记得定义分隔符ROW FORMAT,最重要的是一定要加PARTITIONED BY。写入 (Insert): 忘掉
INSERT VALUES,使用LOAD DATA(搬文件) 或INSERT OVERWRITE SELECT(洗数据)。查询 (Select): 永远带着
WHERE dt='...'。遇到COUNT(DISTINCT)要警惕,尝试用GROUP BY替代。