从一个应用开发者的角度,搞懂大数据查询的完整链路

从一个应用开发者的角度,搞懂大数据查询的完整链路

写在前面

作为一个应用开发者,最近在做数据查询相关的需求时,接触到了 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 引擎通信,中间环节少
  • 适合需要嵌入到业务系统中的场景
三种方式到底怎么选?
对比项HueJava 连 HiveJava 连 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 的核心区别在于:

对比项MapReduceSpark
计算方式每步都写磁盘尽量在内存中计算
速度慢(磁盘 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"把我的任务放到哪个队列去执行",只是参数名不同:

计算引擎指定队列的参数
MapReducemapred.job.queue.namemapreduce.job.queuename
Teztez.queue.name
Sparkspark.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                      │    │
│  └──────────────────────────────────────────────────────────────────────┘    │
└──────────────────────────────────────────────────────────────────────────────┘
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT_Octopus

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值