背景和问题
在使用Spark SQL创建写入Hudi表的时候出现如下错误(错误很长,无关部分省略):
ERROR XJ040: Failed to start database 'metastore_db' with class loader org.apache.spark.sql.hive.client.IsolatedClientLoader$$anon$1@1ebb10fb, see the next exception for details.
...
ERROR XSDB6: Another instance of Derby may have already booted the database /xxx/metastore_db.
...
原因是Derby作为嵌入式数据库,不支持多用户同时访问。但作者并没有同时使用多个spark-sql/spark-shell来操作。百思不得其解。
经过调研得到两个可行的解决方案:
- 升级Spark使用的JDK为JDK17。方法为下载解压JDK17到各个spark节点,然后配置各个节点的
$SPARK_HOME/conf/spark-env.sh
增加export JAVA_HOME=/path/to/jdk17
。最后重新运行任务。 - 放弃使用Derby,使用Hive metastore或者直接使用MySQL作为metastore的存储后端。
两种方式均可解决此问题。下面借此问题,引申出配置Spark metastore的方法。
环境信息
- JDK 8
- Spark 3.x
- MySQL 8.x
- Hive 3.x
Spark SQL自身metastore使用MySQL替换Derby
安装和部署MySQL的步骤因篇幅所限这里省略。可参考其他相关文档。
配置MySQL JDBC驱动
下载和环境中MySQL版本对应的MySQL JDBC驱动,复制到各个Spark节点的$SPARK_HOME/jars
目录中。
MySQL配置
创建metastore专用的数据库:
create database metastore character set utf8mb4;
创建访问metastore元数据专用的用户和权限配置:
create user 'metastore'@'%' identified by 'password';
然后给metastore
用户metastore
数据库的访问权限:
grant all privileges on `metastore`.* to 'metastore'@'%' identified by 'password';
flush privileges;
在MySQL中导入metastore schema
在MySQL所在节点下载Hive 3.x版本,解压后找到scripts/metastore/upgrade/mysql/hive-schema-版本号.mysql.sql
。然后使用metastore
用户进入MySQL执行:
use metastore;
source /path/to/hive/scripts/metastore/upgrade/mysql/hive-schema-版本号.mysql.sql;
配置hive-site.xml
编写如下hive-site.xml
文件,放置在所有Spark节点$SPARK_HOME/conf
目录中。
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://mysql_server_hostname:3306/metastore</value>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.jdbc.Driver</value>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>metastore</value>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>password</value>
</property>
<property>
<name>datanucleus.autoCreateSchema</name>
<value>false</value>
</property>
<property>
<name>datanucleus.fixedDatastore</name>
<value>true</value>
</property>
注意:生产环境为了保证MySQL Schema稳定,避免意外的数据库结构变更,需要设置
datanucleus.fixedDatastore
为true
。如果配置datanucleus.autoCreateSchema
为true
,datanucleus.fixedDatastore
为false
,MySQL metastore库的表结构会自动创建。无需再导入metastore schema。
参考链接:https://docs.databricks.com/data/metastores/external-hive-metastore.html
到这里为止使用MySQL替换Derby作为metastore元数据存储配置完毕。
使用Hive metastore
除了上面的方式之外,生产环境更为通用和稳定的解决方案为统一使用Hive的metastore。下面列出Spark使用Hive metastore的配置步骤。
配置spark-defaults.conf
编辑$SPARK_HOME/conf/spark-defaults.conf
文件,增加如下内容。然后将修改过后的文件分发到所有Spark节点的conf
目录。
spark.sql.hive.metastore.jars /path/to/standalone-metastore/*
spark.sql.hive.metastore.version 3.0
配置项解释:
- spark.sql.hive.metastore.jars: 指定Hive standalone-metastore jar文件所在的路径
- spark.sql.hive.metastore.version: Hive的版本号
配置hive-site.xml
复制包含如下内容的hive-site.xml
到所有Spark节点的$SPARK_HOME/conf
目录。
<configuration>
<property>
<name>hive.exec.scratchdir</name>
<value>/tmp/spark</value>
</property>
<property>
<name>hive.metastore.client.connect.retry.delay</name>
<value>5</value>
</property>
<property>
<name>hive.metastore.client.socket.timeout</name>
<value>1800</value>
</property>
<property>
<name>hive.metastore.uris</name>
<value>thrift://metastore_host:9083</value>
</property>
<property>
<name>hive.server2.enable.doAs</name>
<value>false</value>
</property>
<property>
<name>hive.server2.thrift.http.port</name>
<value>10002</value>
</property>
<property>
<name>hive.server2.thrift.port</name>
<value>10016</value>
</property>
<property>
<name>hive.server2.transport.mode</name>
<value>binary</value>
</property>
<property>
<name>metastore.catalog.default</name>
<value>hive</value>
</property>
</configuration>
需要注意的是需要修改hive.metastore.uris
为真实环境下metastore安装所在节点的URL。hive.server2.thrift.http.port
和hive.server2.thrift.port
也修改为对应服务的端口号。这些配置项可以从Hive集群的hive-site.xml
文件中复制出来直接使用。
到这里Spark使用Hive metastore的配置已完成。
本博客为作者原创,欢迎大家参与讨论和批评指正。如需转载请注明出处。
网友评论