如何同时运行Spark SQL JDBC/ODBC服务器与PySpark?
这个问题我之前帮不少开发者排查过——核心原因就是默认嵌入式Derby是单进程独占模式:JDBC/ODBC服务器启动后已经占用了本地的metastore_db文件,PySpark新建会话时又尝试启动另一个Derby实例访问同一个数据库,自然就触发了冲突错误。
要让两者同时稳定运行,关键是把元数据存储改成共享式的,下面给你两种可行方案,优先推荐第一种生产级方案:
方案1:配置独立Hive Metastore(推荐生产环境)
通过把Spark的元数据存储到外部数据库(比如MySQL、PostgreSQL),让所有Spark进程共享同一个元数据服务,彻底避免Derby的单进程限制。这里以MySQL为例:
1. 准备MySQL数据库
登录你的MySQL服务器,执行以下SQL创建元数据库和授权用户:
-- 创建元数据库 CREATE DATABASE metastore_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 创建Metastore专用用户 CREATE USER 'hiveuser'@'%' IDENTIFIED BY 'your_secure_password'; -- 授予用户数据库权限 GRANT ALL PRIVILEGES ON metastore_db.* TO 'hiveuser'@'%'; FLUSH PRIVILEGES;
2. 添加MySQL JDBC驱动
下载与你的MySQL版本匹配的JDBC驱动(比如mysql-connector-java-8.0.30.jar),将其放到Spark安装目录的jars文件夹下(例如/root/spark/jars/)。
3. 配置Spark和Hive参数
在Spark的conf目录下创建或修改hive-site.xml,添加以下配置:
<configuration> <!-- 数据库连接URL --> <property> <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:mysql://localhost:3306/metastore_db?useSSL=false&serverTimezone=UTC</value> </property> <!-- JDBC驱动类 --> <property> <name>javax.jdo.option.ConnectionDriverName</name> <value>com.mysql.cj.jdbc.Driver</value> </property> <!-- 数据库用户名 --> <property> <name>javax.jdo.option.ConnectionUserName</name> <value>hiveuser</value> </property> <!-- 数据库密码 --> <property> <name>javax.jdo.option.ConnectionPassword</name> <value>your_secure_password</value> </property> <!-- Hive数据仓库存储路径(可自定义) --> <property> <name>hive.metastore.warehouse.dir</name> <value>/user/hive/warehouse</value> </property> <!-- 关闭Schema验证(首次初始化时建议开启,稳定后可关闭) --> <property> <name>hive.metastore.schema.verification</name> <value>false</value> </property> </configuration>
同时修改spark-defaults.conf,指定使用Hive Metastore:
spark.sql.catalogImplementation hive # 注意:这里的版本要和Spark自带的Hive版本匹配(查看spark/jars下的hive-metastore-*.jar版本) spark.sql.hive.metastore.version 3.1.2
4. 初始化Metastore Schema
在Spark的bin目录下执行Schema初始化命令,创建元数据库表结构:
./schematool -initSchema -dbType mysql -verbose
5. 重启服务
关闭之前运行的JDBC/ODBC服务器,重新启动:
./start-thriftserver.sh
现在再运行PySpark执行df.write.saveAsTable(),所有Spark进程都会连接到同一个共享的Hive Metastore,不会再出现Derby冲突。
方案2:临时使用Derby网络模式(仅适合测试)
如果只是临时测试不想部署外部数据库,可以把Derby改成网络服务器模式,允许多个进程连接:
1. 启动Derby网络服务器
找到Spark自带的derbyrun.jar(在jars目录下),执行命令启动Derby服务器:
java -jar /root/spark/jars/derbyrun.jar server start
2. 配置Spark连接网络Derby
修改spark-defaults.conf,添加以下配置:
spark.sql.catalogImplementation hive spark.hadoop.javax.jdo.option.ConnectionURL jdbc:derby://localhost:1527/metastore_db;create=true spark.hadoop.javax.jdo.option.ConnectionDriverName org.apache.derby.jdbc.ClientDriver
3. 重启服务
关闭并重新启动JDBC/ODBC服务器,然后运行PySpark即可同时使用。
注意事项
- 生产环境强烈推荐方案1,Derby网络模式的性能、稳定性和扩展性都远不如MySQL/PostgreSQL这类成熟数据库。
- 确保Hive Metastore版本与Spark自带的Hive版本匹配,否则可能出现兼容性报错。
- 如果使用Spark 3.x+版本,也可以考虑使用Spark内置的
spark_catalog,但Hive Metastore仍是跨进程共享元数据的最成熟方案。
内容的提问来源于stack exchange,提问作者Mikhail Venkov

