EMR集群配置外部metastore后理解Spark与Hive元数据配置
在 EMR 集群中创建时通过 hive-site classification 配置了 external metastore(外部 MySQL),但发现 Spark 配置目录下也存在 hive-site.xml,且内容与 Hive 侧不完全一致。本文分析两者的配置差异及 Spark 访问 Hive Metastore 的实际链路。
Hive 侧配置 (/etc/hive/conf/hive-site.xml)
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://172.31.14.46:3306/hive_metastore?createDatabaseIfNotExist=true</value>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>org.mariadb.jdbc.Driver</value>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>hive</value>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>hive</value>
</property>
Spark 侧配置 (/etc/spark/conf/hive-site.xml)
<property>
<name>hive.metastore.uris</name>
<value>thrift://ip-192-168-24-10.cn-north-1.compute.internal:9083</value>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>hive</value>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>hive</value>
</property>
<property>
<name>hive.server2.thrift.url</name>
<value>jdbc:hive2://ip-192-168-24-10.cn-north-1.compute.internal:10000</value>
</property>
<property>
<name>hive.metastore.connect.retries</name>
<value>15</value>
</property>
Spark 侧缺少 ConnectionURL 和 ConnectionDriverName,因此无法直接连接 MySQL 数据库。Spark 真正依赖的是 hive.metastore.uris,通过 Thrift 协议连接 Hive Metastore 服务。
访问链路如下
- Spark 通过
hive.metastore.uris连接本地 master 节点上的 Hive Metastore Thrift 服务(端口 9083) - Hive Metastore 服务通过自身的
hive-site.xml中的ConnectionURL连接外部 MySQL
Spark → (thrift:9083) → Hive Metastore Service → (jdbc:3306) → MySQL (外部数据库)
EMR 在集群配置阶段,会将用户通过 hive-site classification 传入的参数同时分发到 Hive 和 Spark 的 conf 目录,但会做裁剪:
ConnectionURL和Driver被有意从 Spark 侧去掉(避免 Spark 绕过 Metastore 服务直接连数据库)
Spark 启动时会自动加载 $SPARK_CONF_DIR(即 /etc/spark/conf/)下的 hive-site.xml。只要 classpath 中存在该文件,SparkSession 在启用 enableHiveSupport() 时就会读取其中的配置。
Hive 和 Spark 建表插入数据
分别用 Hive 和 Spark 在 test_db 中创建表并插入数据,然后检查 MySQL metastore 和 HDFS 的变化。
# Hive 建表插入
sudo -u hadoop hive -e "
CREATE DATABASE IF NOT EXISTS test_db;
CREATE TABLE test_db.hive_test_table (id INT, name STRING, created_at STRING)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
INSERT INTO test_db.hive_test_table VALUES (1,'alice','2026-03-24'),(2,'bob','2026-03-24'),(3,'charlie','2026-03-24');
"
# Spark 建表插入
sudo -u hadoop spark-sql -e "
CREATE TABLE test_db.spark_test_table (id INT, name STRING, created_at STRING)
USING hive OPTIONS (fileFormat 'textfile', fieldDelim ',');
INSERT INTO test_db.spark_test_table VALUES (4,'dave','2026-03-24'),(5,'eve','2026-03-24'),(6,'frank','2026-03-24');
"
建表后查询 MySQL,可以看到两张表的元数据都写入了同一个 metastore:
DBS 表:新增 test_db
| DB_ID | NAME | LOCATION |
|---|---|---|
| 1 | default | hdfs://.../user/hive/warehouse |
| 2 | test_db | hdfs://.../user/hive/warehouse/test_db.db |
TBLS 表:Hive 和 Spark 各创建了一张表
| TBL_ID | DB | 表名 | 类型 | 创建时间 | 创建者 |
|---|---|---|---|---|---|
| 2 | test_db | hive_test_table | MANAGED_TABLE | 06:29:30 | Hive |
| 3 | test_db | spark_test_table | MANAGED_TABLE | 06:30:13 | Spark |
COLUMNS_V2 表:两张表各 3 列 (id INT, name STRING, created_at STRING)
SDS 表(存储描述):两张表都存储在 HDFS,格式均为 TextInputFormat
MySQL metastore 只存元数据(表名、列定义、存储位置等),真正的数据存在 HDFS 上:
/user/hive/warehouse/test_db.db/
├── hive_test_table/
│ └── 000000_0 # Hive(Tez) 写入,单个文件
└── spark_test_table/
├── part-00000-ae729811-... # Spark 写入的 partition 0
└── part-00001-ae729811-... # Spark 写入的 partition 1
文件内容都是纯文本 CSV:
# hive_test_table/000000_0
1,alice,2026-03-24
2,bob,2026-03-24
3,charlie,2026-03-24
# spark_test_table/part-00000-*
4,dave,2026-03-24
# spark_test_table/part-00001-*
5,eve,2026-03-24
6,frank,2026-03-24
Hive vs Spark 写入差异
| 对比项 | Hive (Tez) | Spark |
|---|---|---|
| 文件命名 | 000000_0 |
part-NNNNN-<uuid> |
| 文件数量 | 1 个(单 reducer) | 2 个(多 partition) |
| 数据格式 | 相同(CSV 文本) | 相同(CSV 文本) |
| 元数据存储 | 同一个 MySQL metastore | 同一个 MySQL metastore |
这验证了 Hive 和 Spark 通过同一个 external metastore(MySQL)管理元数据,两者创建的表互相可见、共享同一个元数据存储,实际数据都存放在 HDFS 的 /user/hive/warehouse/ 下。

浙公网安备 33010602011771号