从一个应用开发者的角度,搞懂大数据查询的完整链路
写在前面
作为一个应用开发者,最近在做数据查询相关的需求时,接触到了 Hue、Hive 表、Spark SQL、HDFS、MapReduce、YARN 队列这些概念。一开始真的是一头雾水——它们之间到底是什么关系?我写的一条 SQL,到底经历了什么才拿到结果?
经过一段时间的学习和摸索,终于把整条链路搞清楚了。今天从一个应用开发者的视角,把这条链路从头到尾串一遍,希望能帮到和我一样困惑的朋友。
一切的起点:我在 Hue 里写了一条 SQL
作为应用开发者,我的日常工作就是写代码、查数据。假设现在有一个需求:查询用户行为日志表中,某一天金额大于 100 的记录。
我最开始接触大数据,就是在浏览器里打开了一个网页工具——Hue。
Hue(Hadoop User Experience)是一个基于 Web 的图形化工具,你可以理解为大数据世界的"网页版 SQL 客户端"。打开浏览器,登录之后就可以直接写 SQL、查数据,不需要写任何代码。
我写的 SQL 大概长这样:
SELECT * FROM user_log
WHERE dt = '2026-09-12' AND amount > 100
看起来很简单,和平时写 MySQL 查询没什么区别。但问题是——这条 SQL 提交之后,到底发生了什么?
而且更让我困惑的是,我至少有三种方式可以执行这条 SQL:
- 在 Hue 网页里直接执行
- 在我的 Java 程序里,通过 Hive JDBC 来执行
- 在我的 Java 程序里,通过 Spark 来执行
这三种方式有什么区别?底层走的链路一样吗?
先搞清楚几个基础概念
在讲三种方式之前,先把几个核心概念搞清楚。
Hive 表到底是什么?
在 MySQL 里,表就是表,数据就存在数据库里,增删改查都是数据库自己搞定。
但 Hive 表不一样。Hive 表其实是一个逻辑概念,它本身不存数据,而是通过一张"账本"(元数据)告诉你:
- 这个表叫什么名字
- 有哪些字段、什么类型
- 数据存在 HDFS 的哪个路径下
- 用了什么存储格式(ORC、Parquet、TextFile 等)
- 有没有分区,分区字段是什么
简单说:Hive 表 = 一张"说明书",告诉你数据在哪、长什么样。真正的数据在 HDFS 上。
HDFS 又是什么?
HDFS(Hadoop Distributed File System)是 Hadoop 的分布式文件系统。你可以把它理解成一个超大的网络硬盘,数据被切分成很多块,分散存储在多台机器上。
比如我的 user_log 表,实际在 HDFS 上可能是这样的:
/warehouse/user_log/
dt=2026-09-11/
000000_0.orc
000001_0.orc
dt=2026-09-12/
000000_0.orc
000001_0.orc
dt=2026-09-13/
000000_0.orc
每个 .orc 文件就是一块实际的数据文件,里面存着真正的用户行为记录。
Hive Metastore 是干什么的?
Hive Metastore 就是那张"账本"的服务。它底层用的是 MySQL,里面存着所有 Hive 表的元数据。
当计算引擎(不管是 Spark 还是 Hive)要查一张表时,第一步一定是先去问 Metastore:
- 这个表有哪些字段?
- 数据在 HDFS 的哪个路径?
- 有没有分区?分区字段是什么?
Metastore 回答完之后,计算引擎才知道该去哪里读数据、怎么读。
三种访问方式对比:Hue vs Java 连 Hive vs Java 连 Spark
这是我最开始最困惑的地方。同样是执行一条 SQL,为什么有三种方式?它们的区别到底是什么?
先上一张总览图:
┌─────────────────────────────────────────────────────────────────────┐
│ 最顶层:三种访问方式 │
│ │
│ 方式一:Hue 方式二:Java 连 Hive 方式三:Java 连 Spark│
│ │
│ ┌────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ 浏览器网页 │ │ 你的 Java 程序 │ │ 你的 Java 程序 │ │
│ │ │ │ │ │ │ │
│ │ 打开浏览器 │ │ JDBC 连接 │ │ SparkSession │ │
│ │ 输入 SQL │ │ jdbc:hive2:// │ │ .builder() │ │
│ │ 点击执行 │ │ host:10000 │ │ .enableHive │ │
│ │ │ │ │ │ Support() │ │
│ │ │ │ Statement │ │ .getOrCreate() │ │
│ │ │ │ .executeQuery() │ │ │ │
│ └───────┬────────┘ └────────┬─────────┘ └────────┬─────────┘ │
│ │ │ │ │
│ ↓ ↓ ↓ │
│ ┌──────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ Hue Web 服务 │ │ HiveServer2 │ │ Spark 引擎 │ │
│ │ (Python Web 应用) │ │ (前台接待) │ │ (计算引擎) │ │
│ │ 端口 8888 │ │ 端口 10000 │ │ │ │
│ └────────┬─────────┘ └───────┬────────┘ └────────┬───────┘ │
│ │ │ │ │
│ ↓ ↓ │ │
│ ┌──────────────────┐ ┌────────────────┐ │ │
│ │ 转发给引擎 │ │ Hive Driver │ │ │
│ │ (Hive/HiveServer2│ │ (解析SQL/优化) │ │ │
│ │ 或 Livy→Spark) │ └───────┬────────┘ │ │
│ └────────┬─────────┘ │ │ │
└───────────┼────────────────────┼──────────────────────┼─────────────┘
│ │ │
└────────────────────┼──────────────────────┘
│
┌───────────┴───────────┐
│ 下面都是同一套东西 │
└───────────┬───────────┘
↓
┌───────────────────────┐
│ Hive Metastore │
│ (元数据 / 账本) │
│ │
│ 表名、字段、分区 │
│ 存储路径、格式 │
└───────────┬───────────┘
│ JDBC
↓
┌───────────────────────┐
│ MySQL │
│ (元数据存储) │
└───────────────────────┘
┌───────────────────────┐
│ HDFS │
│ (实际数据文件) │
│ │
│ /warehouse/ │
│ user_log/ │
│ dt=2026-09-12/ │
│ 000000_0.orc │
└───────────────────────┘
可以看到,不管用哪种方式,最终查的都是同一个 Metastore、同一个 HDFS。区别只在于"最上面那一层"——谁接收你的 SQL、谁来执行计算。
下面分别讲清楚每种方式。
方式一:Hue(图形化界面)
Hue 的链路是这样的:
浏览器(你在网页上写 SQL)
↓ HTTP 请求
Hue Web 服务(Python 后端,端口 8888)
↓ 根据配置,转发给对应的引擎
↓
├── 如果配置连 HiveServer2 → 走 Hive 链路(MapReduce/Tez 执行)
└── 如果配置连 Livy → 走 Spark 链路(Spark 引擎执行)
这里有一个很容易踩的坑:
Hue 里虽然可以选择"SparkSQL 编辑器",但不一定真的在用 Spark 执行。如果 Hue 的配置中 SparkSQL 的 interface 设置的是 hiveserver2,那实际上走的还是 Hive 的链路(Tez 引擎),并没有真正用到 Spark。只有配置了 Livy 接口,才会真正走 Spark 引擎。
特点:
- 不需要写代码,打开浏览器就能用,对数据分析师非常友好
- 支持 SQL 语法高亮、自动补全、结果下载(Excel 格式)
- 还能浏览 HDFS 文件、查看任务状态、管理 Oozie 工作流等
- 适合日常开发调试、临时查询、数据探索
方式二:Java 连 Hive
这种方式是 Java 程序通过 JDBC 协议连接 HiveServer2,由 Hive 来执行 SQL。
Class.forName("org.apache.hive.jdbc.HiveDriver");
Connection conn = DriverManager.getConnection(
"jdbc:hive2://host:10000/default"
);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(
"SELECT * FROM user_log WHERE dt = '2026-09-12' AND amount > 100"
);
while (rs.next()) {
System.out.println(rs.getString("user_id"));
}
链路是这样的:
Java 程序
↓ JDBC 连接 HiveServer2(端口 10000)
HiveServer2(前台接待)
↓ 转发给 Hive Driver
Hive Driver
↓ 解析 SQL
↓ 问 Metastore 拿表结构
↓ 生成执行计划
↓ 翻译成 MapReduce / Tez 任务
MapReduce / Tez 任务
↓ 去 HDFS 读数据
↓ 每步计算结果写磁盘
↓ 最终结果写回 HDFS
结果返回给 HiveServer2
↓ JDBC 返回
Java 程序拿到结果
特点:
- 计算引擎是 MapReduce 或 Tez,每步都写磁盘,速度慢
- 中间环节多(Java → HiveServer2 → Driver → MR/Tez → HDFS)
- 适合不需要嵌入业务系统、只需要跑 SQL 的场景
方式三:Java 连 Spark
这是我在实际项目中最常用的方式。Java 程序通过 SparkSession 连接 Spark 引擎,由 Spark 来执行 SQL。
SparkSession spark = SparkSession.builder()
.appName("MyApp")
.master("yarn")
.enableHiveSupport() // 告诉 Spark 去连 Hive Metastore
.getOrCreate();
Dataset<Row> result = spark.sql(
"SELECT * FROM user_log WHERE dt = '2026-09-12' AND amount > 100"
);
result.show();
链路是这样的:
Java 程序
↓ SparkSession(连接 Spark 引擎)
Spark 引擎
↓ 解析 SQL(Catalyst 优化器)
↓ 问 Metastore 拿表结构
↓ 去 HDFS 读数据
↓ 在内存中计算(过滤、聚合等)
↓ 返回结果
Java 程序拿到结果
特点:
- 计算引擎是 Spark,用内存计算,速度快
- Java 程序直接和 Spark 引擎通信,中间环节少
- 适合需要嵌入到业务系统中的场景
三种方式到底怎么选?
| 对比项 | Hue | Java 连 Hive | Java 连 Spark |
|---|---|---|---|
| 使用方式 | 打开浏览器写 SQL | 写 Java 代码 | 写 Java 代码 |
| 连接目标 | Hue Web(端口 8888) | HiveServer2(端口 10000) | Spark 引擎 |
| 计算引擎 | 取决于配置(Hive 或 Spark) | MapReduce / Tez(磁盘计算) | Spark(内存计算) |
| 速度 | 取决于底层引擎 | 慢 | 快 |
| 适用场景 | 日常开发调试、数据探索 | 简单 SQL 查询 | 嵌入业务系统 |
| 需要写代码吗 | 不需要 | 需要 | 需要 |
| 谁在用 | 数据分析师、开发人员 | 应用开发者 | 应用开发者 |
一句话总结:Java 连 Spark 和 Java 连 Hive 的区别在于"计算引擎不同";Hue 和前两者的区别在于"Hue 是一个图形化前端,不是计算引擎"。
那 MapReduce 又是什么?
这是我一开始最困惑的地方。既然 Spark 能算,为什么还有 MapReduce?
其实 MapReduce 是 Hadoop 原生的计算框架,它是大数据领域最早的计算引擎。它的计算模型分两步:
- Map 阶段:把输入数据切分成小块,每台机器各自处理一部分,输出中间结果
- Reduce 阶段:把中间结果汇总,做聚合、排序等操作,输出最终结果
用我的 SQL 举例,如果用 MapReduce 来执行:
Map 阶段:
每台机器读取一部分 HDFS 文件
逐行判断 WHERE 条件(dt='2026-09-12' AND amount > 100)
满足条件的数据输出为中间结果
Reduce 阶段:
汇总所有 Map 的输出
如果有 GROUP BY,在这里做聚合
最终结果写回 HDFS
MapReduce 和 Spark 的核心区别在于:
| 对比项 | MapReduce | Spark |
|---|---|---|
| 计算方式 | 每步都写磁盘 | 尽量在内存中计算 |
| 速度 | 慢(磁盘 IO 多) | 快(内存迭代) |
| 适用场景 | 大批量离线任务 | 交互式查询、迭代计算 |
| 编程模型 | Map + Reduce 两步 | DAG 有向无环图,更灵活 |
打个比方:MapReduce 就像每次做完一步都要把结果写到纸上再交给下一个人;Spark 则是做完一步直接口头告诉下一个人,省去了写纸的时间。
还有一个绕不开的东西:YARN 队列
这是我每次提交任务时都要填的一个字段,但一直不太清楚它到底是什么。
YARN 是什么?
YARN(Yet Another Resource Negotiator)是 Hadoop 的资源管理器。你可以把它理解成整个集群的"资源调度中心"——谁要用多少 CPU、多少内存,都由 YARN 来分配。
那队列又是什么?
队列就是 YARN 里的资源分区。你可以把它理解成"排队窗口":
- 整个集群的资源是有限的(比如 100 个 CPU 核心、200G 内存)
- 如果所有人都往一个队列里挤,资源就会互相抢
- 所以 YARN 把资源划分成多个队列,每个队列有自己的资源配额
打个比方:
集群资源 = 一家银行
队列 = 银行里的不同窗口
├── VIP 窗口(给重要业务,资源多)
├── 普通窗口(给日常查询,资源少)
└── 开发窗口(给开发人员测试用)
为什么需要队列?
- 资源隔离:不同业务、不同团队的任务互不干扰,不会因为某个大任务把整个集群资源占满
- 优先级管理:重要任务放到高优先级队列,保证资源充足
- 按业务划分:比如电商业务一个队列、广告业务一个队列,各自管各自的
队列是怎么工作的?
当你在 Hue 里提交一条 SQL,或者在 Java 程序里提交一个 Spark 任务时,任务会被提交到指定的队列。YARN 调度器会根据队列的配置,分配相应的资源(CPU、内存)给这个任务。
你的 SQL / Spark 任务
↓ 提交到指定队列
YARN 调度器
↓ 根据队列配置分配资源
↓
├── 队列 A(60% 资源)→ 给电商业务
├── 队列 B(30% 资源)→ 给广告业务
└── 队列 C(10% 资源)→ 给开发测试
队列名的层级结构
实际工作中,你填的队列名可能长这样:
mapred.job.queue.name=root.yarn.kp.xray
这个 root.yarn.kp.xray 是一个多级队列路径,说明公司的 YARN 队列是树状结构的:
root(根队列)
└── yarn
└── kp
└── xray ← 你的任务提交到这个叶子队列
YARN 的队列支持嵌套,用 . 分隔层级。root 是 YARN 的默认根队列,后面每一级都是子队列。你提交的 root.yarn.kp.xray 就是告诉 YARN:把任务放到 root → yarn → kp → xray 这个叶子队列里执行。
不同引擎,指定队列的参数名不一样
这是实际开发中很容易搞混的一个点。不同计算引擎各自定义了自己的配置参数来指定队列,做的事情完全一样——都是告诉 YARN"把我的任务放到哪个队列去执行",只是参数名不同:
| 计算引擎 | 指定队列的参数 |
|---|---|
| MapReduce | mapred.job.queue.name 或 mapreduce.job.queuename |
| Tez | tez.queue.name |
| Spark | spark.yarn.queue |
在 SQL 里指定队列(Hive on MapReduce):
set mapred.job.queue.name=root.yarn.kp.xray;
select * from user_log where dt = '2026-09-12';
在 Java 代码里指定队列(Spark):
SparkSession spark = SparkSession.builder()
.appName("MyApp")
.master("yarn")
.config("spark.yarn.queue", "root.yarn.kp.xray") // 指定队列
.enableHiveSupport()
.getOrCreate();
在 SQL 里指定队列(Hive on Tez):
set tez.queue.name=root.yarn.kp.xray;
select * from user_log where dt = '2026-09-12';
所以本质上没有矛盾,只是引擎不同、参数名不同而已。如果你平时用的引擎底层走的是 MapReduce,那填 mapred.job.queue.name 就是对的。
队列的配置长什么样?
队列的配置通常在 capacity-scheduler.xml 文件中,主要配置项包括:
- capacity:队列的资源占比(比如 60%)
- maximum-capacity:队列最大能占用的资源上限(比如 80%)
- user-limit-factor:单个用户最多能用队列资源的多少倍
- state:队列状态(RUNNING 或 STOPPED)
- acl_submit_applications:哪些用户可以往这个队列提交任务
一句话总结:YARN 是集群的资源管理器,队列是 YARN 里的资源分区,用来隔离和管理不同业务的计算资源。你每次提交任务时填的队列名,就是告诉 YARN"把我的任务放到哪个资源分区去执行"。不同引擎用不同的参数名来指定队列,但底层都是提交给同一个 YARN 调度器。
一张图总结:从最顶层到最底层的完整链路
┌──────────────────────────────────────────────────────────────────────────────┐
│ 【顶层】用户 / 应用入口 │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────────────────────────┐ │
│ │ Java/Python │ │ BI 工具 │ │ Hue / DBeaver / Superset │ │
│ │ (JDBC/ODBC) │ │ (Tableau等) │ │ (浏览器 SQL Editor) │ │
│ └──────┬──────┘ └──────┬──────┘ └───────────────┬──────────────────┘ │
│ │ │ │ │
└─────────┼─────────────────┼───────────────────────────┼──────────────────────┘
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 【接入层】统一网关 / 协议适配 │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ HiveServer2 (Thrift/JDBC) ←→ Livy (REST API for Spark) │ │
│ │ * 认证(Kerberos/LDAP) * 会话管理 * SQL解析 & 权限校验(Ranger) │ │
│ └──────────────────────────────┬───────────────────────────────────────┘ │
└─────────────────────────────────┼────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 【计算层】执行引擎 │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ Spark Engine │ │ Tez / MR Engine │ │ Presto / Trino (可选) │ │
│ │ (内存/DAG计算) │ │ (磁盘/批处理) │ │ (交互式MPP查询) │ │
│ └────────┬─────────┘ └────────┬─────────┘ └────────────┬─────────────┘ │
│ │ │ │ │
│ └─────────────────────┼──────────────────────────┘ │
│ ▼ │
│ YARN / K8s (资源调度 & 容器管理) │
│ Container → Executor/Task → Shuffle → Result │
└─────────────────────────────────┼────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 【元数据层】MetaStore │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ Hive MetaStore Service (HMS) │ │
│ │ * 库/表/分区定义 * Schema版本 * 统计信息 * 血缘关系 │ │
│ │ * 后端: MySQL / PostgreSQL / Oracle │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────┼────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 【存储层】分布式文件系统 │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ HDFS / S3 / OSS / Ceph │ │
│ │ * 文件格式: Parquet / ORC / Avro / Text │ │
│ │ * 压缩: Snappy / Zstd / Gzip │ │
│ │ * 分区目录: /db/table/dt=2026-09-14/part=00000 │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘

1229

被折叠的 条评论
为什么被折叠?



