更新时间:2024-09-19 GMT+08:00

使用Spark作业访问DLI元数据

操作场景

DLI支持用户编写代码创建Spark作业来创建数据库、创建DLI表或OBS表和插入表数据等操作。本示例完整的演示通过编写java代码、使用Spark作业创建数据库、创建表和插入表数据的详细操作,帮助您在DLI上进行作业开发。

该功能公测阶段,如需使用请提交工单申请开通“使用Spark作业访问DLI元数据”的使用权限。

约束限制

  • 如果使用Spark 3.1访问元数据,则必须新建队列。
  • 不支持的场景:
    • 在SQL作业中创建了数据库(database),编写程序代码指定在该数据库下创建表。

      例如在DLI的SQL编辑器中的某SQL队列下,创建了数据库testdb。后续通过编写程序代码在testdb下创建表testTable,编译打包后提交的Spark Jar作业则会运行失败。

  • 支持的场景
    • 在SQL作业中创建数据库(database),表(table) , 通过SQL或Spark程序作业读取插入数据。
    • 在Spark程序作业中创建数据库(database),表(table), 通过SQL或Spark程序作业读取插入数据。

环境准备

在进行Spark 作业访问DLI元数据开发前,请准备以下开发环境。

表1 Spark Jar作业开发环境

准备项

说明

操作系统

Windows系统,支持Windows7以上版本。

安装JDK

JDK使用1.8版本。

安装和配置IntelliJ IDEA

IntelliJ IDEA为进行应用开发的工具,版本要求使用2019.1或其他兼容版本。

安装Maven

开发环境的基本配置。用于项目管理,贯穿软件开发生命周期。

开发流程

DLI进行Spark作业访问DLI元数据开发流程参考如下:
图1 Spark作业访问DLI元数据开发流程
表2 开发流程说明

序号

阶段

操作界面

说明

1

创建DLI通用队列

DLI控制台

创建作业运行的DLI队列。

2

OBS桶文件配置

OBS控制台

  • 如果是创建OBS表,则需要上传文件数据到OBS桶下。
  • 配置Spark创建表的元数据信息的存储路径。该文件夹路径用来存储Spark创建表的元数据信息“spark.sql.warehouse.dir”。

3

新建Maven工程,配置pom文件

IntelliJ IDEA

参考样例代码说明,编写程序代码创建DLI表或OBS表。

4

编写程序代码

5

调试,编译代码并导出Jar包

6

上传Jar包到OBS和DLI

OBS控制台

DLI控制台

将生成的Spark Jar包文件上传到OBS目录下和DLI程序包中。

7

创建Spark Jar作业

DLI控制台

在DLI控制台创建Spark Jar作业并提交运行作业。

8

查看作业运行结果

DLI控制台

查看作业运行状态和作业运行日志。

步骤1:创建DLI通用队列

提交Spark作业需要先创建队列,本例创建名为“sparktest”的通用队列。

  1. 登录DLI管理控制台。
  2. 在左侧导航栏单击“资源管理 > 弹性资源池”,可进入弹性资源池管理页面。
  3. 在弹性资源池管理界面,单击界面右上角的“购买弹性资源池”。
  4. 在“购买弹性资源池”界面,填写具体的弹性资源池参数。
  5. 本例在华东-上海二区域购买按需计费的弹性资源池。相关参数说明如表3所示。
    表3 参数说明

    参数名称

    参数说明

    配置样例

    区域

    选择弹性资源池所在区域。

    华东-上海二

    项目

    每个区域默认对应一个项目,由系统预置。

    系统默认项目

    名称

    弹性资源池名称。

    dli_resource_pool

    规格

    选择弹性资源池规格。

    标准版

    CU范围

    弹性资源池最大最小CU范围。

    64-64

    网段

    规划弹性资源池所属的网段。如需使用DLI增强型跨源,弹性资源池网段与数据源网段不能重合。弹性资源池网段设置后不支持更改

    172.16.0.0/19

    企业项目

    选择对应的企业项目。

    default

  6. 参数填写完成后,单击“立即购买”,在界面上确认当前配置是否正确。
  7. 单击“提交”完成弹性资源池的创建。
  8. 在弹性资源池的列表页,选择要操作的弹性资源池,单击操作列的“添加队列”。
  9. 配置队列的基础配置,具体参数信息如下。
    表4 弹性资源池添加队列基础配置

    参数名称

    参数说明

    配置样例

    名称

    弹性资源池添加的队列名称。

    dli_queue_01

    类型

    选择创建的队列类型。

    • 执行SQL作业请选择SQL队列。
    • 执行Flink或Spark作业请选择通用队列。

    _

    执行引擎

    SQL队列可以选择队列引擎为Spark或者Trino。

    _

    企业项目

    选择对应的企业项目。

    default

  10. 单击“下一步”,配置队列的扩缩容策略。

    单击“新增”,可以添加不同优先级、时间段、“最小CU”和“最大CU”扩缩容策略。

    本例配置的扩缩容策略如图2所示。
    图2 添加队列时配置扩缩容策略
    表5 扩缩容策略参数说明

    参数名称

    参数说明

    配置样例

    优先级

    当前弹性资源池中的优先级数字越大表示优先级越高。本例设置一条扩缩容策略,默认优先级为1。

    1

    时间段

    首条扩缩容策略是默认策略,不能删除和修改时间段配置。

    即设置00-24点的扩缩容策略。

    00-24

    最小CU

    设置扩缩容策略支持的最小CU数。

    16

    最大CU

    当前扩缩容策略支持的最大CU数。

    64

  11. 单击“确定”完成添加队列配置。

步骤2:OBS桶文件配置

  1. 如果需要创建OBS表,则需要先上传数据到OBS桶目录下。
    本次演示的样例代码创建了OBS表,测试数据内容参考如下示例,创建名为的testdata.csv文件。
    12,Michael
    27,Andy
    30,Justin
  2. 进入OBS管理控制台,在“桶列表”下,单击已创建的OBS桶名称,本示例桶名为“dli-test-obs01”。
  3. 单击“上传对象”,将testdata.csv文件上传到OBS桶根目录下。
  4. 在OBS桶根目录下,单击“新建文件夹”,创建名为“warehousepath”的文件夹。该文件夹路径用来存储Spark创建表的元数据信息“spark.sql.warehouse.dir”。

步骤3:新建Maven工程,配置pom依赖

以下通过IntelliJ IDEA 2020.2工具操作演示。
  1. 打开IntelliJ IDEA,选择“File > New > Project”。
    图3 新建Project
  2. 选择Maven,Project SDK选择1.8,单击“Next”。
    图4 选择SDK
  3. 定义样例工程名和配置样例工程存储路径,单击“Finish”完成工程创建。
    图5 新建工程

    如上图所示,本示例创建Maven工程名为:SparkJarMetadata,Maven工程路径为:“D:\DLITest\SparkJarMetadata”。

  4. 在pom.xml文件中添加如下配置。
    <dependencies>
            <dependency>
                <groupId>org.apache.spark</groupId>
                <artifactId>spark-sql_2.11</artifactId>
                <version>2.3.2</version>
            </dependency>
    </dependencies>
    图6 修改pom.xml文件
  5. 在工程路径的“src > main > java”文件夹上鼠标右键,选择“New > Package”,新建Package和类文件。
    图7 新建Package

    Package根据需要定义,本示例定义为:“com.huawei.dli.demo”,完成后回车。

    在包路径下新建Java Class文件,本示例定义为:DliCatalogTest。
    图8 新建Java Class文件

步骤4:编写代码

编写DliCatalogTest程序创建数据库、DLI表和OBS表。

完整的样例请参考Java样例代码,样例代码分段说明如下:

  1. 导入依赖的包。
    import org.apache.spark.sql.SparkSession;
  2. 创建SparkSession会话。

    创建SparkSession会话时需要指定Spark参数:"spark.sql.session.state.builder"、"spark.sql.catalog.class"和"spark.sql.extensions",按照样例配置即可。

    • Spark2.3.x版本
      SparkSession spark = SparkSession
                      .builder()
                      .config("spark.sql.session.state.builder", "org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder")
                      .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog")
                      .config("spark.sql.extensions","org.apache.spark.sql.DliSparkExtension")
                      .appName("java_spark_demo")
                      .getOrCreate();
    • Spark2.4.x版本
      SparkSession spark = SparkSession 
                       .builder() 
                       .config("spark.sql.session.state.builder", "org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder") 
                       .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") 
                       .config("spark.sql.extensions","org.apache.spark.sql.DliSparkExtension") 
                       .config("spark.sql.hive.implementation","org.apache.spark.sql.hive.client.DliHiveClientImpl")
                       .appName("java_spark_demo") 
                       .getOrCreate();
    • Spark3.1.x版本
      SparkSession spark = SparkSession
                      .builder()
                      .config("spark.sql.session.state.builder", "org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder")
                      .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog")
                      .config("spark.sql.extensions","org.apache.spark.sql.DliSparkExtension")
                      .appName("java_spark_demo")
                      .getOrCreate();
    • Spark3.3.x版本
      SparkSession spark = SparkSession
                 .builder()           
                 .config("spark.sql.session.state.builder", "org.apache.spark.sql.hive.DliLakeHouseBuilder")           
                 .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.DliLakeHouseCatalog")           
                 .appName("java_spark_demo")           
                 .getOrCreate();   
  3. 创建数据库。
    如下样例代码演示,创建名为test_sparkapp的数据库。
    spark.sql("create database if not exists test_sparkapp").collect();
  4. 创建DLI表并插入测试数据。
    spark.sql("drop table if exists test_sparkapp.dli_testtable").collect();
    spark.sql("create table test_sparkapp.dli_testtable(id INT, name STRING)").collect();
    spark.sql("insert into test_sparkapp.dli_testtable VALUES (123,'jason')").collect();
    spark.sql("insert into test_sparkapp.dli_testtable VALUES (456,'merry')").collect();
  5. 创建OBS表。如下示例中的OBS路径需要根据步骤2:OBS桶文件配置中的实际数据路径修改。
    spark.sql("drop table if exists test_sparkapp.dli_testobstable").collect();
    spark.sql("create table test_sparkapp.dli_testobstable(age INT, name STRING) using csv options (path 'obs://dli-test-obs01/testdata.csv')").collect();
  6. 关闭SparkSession会话spark。
    spark.stop();

步骤5:调试、编译代码并导出Jar包

  1. 双击IntelliJ IDEA工具右侧的“Maven”,参考下图分别双击“clean”、“compile”对代码进行编译。
    编译成功后,双击“package”对代码进行打包。
    图9 编译打包
    打包成功后,生成的Jar包会放到target目录下,以备后用。本示例将会生成到:“D:\DLITest\SparkJarMetadata\target”下名为“SparkJarMetadata-1.0-SNAPSHOT.jar”。
    图10 导出jar包

步骤6:上传Jar包到OBS和DLI下

  • Spark 3.3及以上版本:

    仅支持在创建Spark作业时,配置“应用程序”,从OBS选择作业所需的Jar包。

    1. 登录OBS控制台,将生成的Jar包文件上传到OBS路径下。
    2. 登录DLI控制台,选择“作业管理 > Spark作业”。
    3. 单击操作列“编辑”。
    4. 编辑“应用程序”,选择1上传的OBS地址。
      图11 配置应用程序
  • Spark 3.3以下版本:

    分别上传Jar包到OBS和DLI下。

    1. 登录OBS控制台,将生成的Jar包文件上传到OBS路径下。
    2. 将Jar包文件上传到DLI的程序包管理中,方便后续统一管理。
      1. 登录DLI管理控制台,单击“数据管理 > 程序包管理”。
      2. 在“程序包管理”页面,单击右上角的“创建程序包”。
      3. 在“创建程序包”对话框,配置以下参数。
        1. 包类型:选择“JAR”。
        2. OBS路径:程序包所在的OBS路径。
        3. 分组设置和组名称根据情况选择设置,方便后续识别和管理程序包。
      4. 单击“确定”,完成创建程序包。
        图12 创建程序包

步骤7:创建Spark Jar作业

  1. 登录DLI控制台,单击“作业管理 > Spark作业”。
  2. 在“Spark作业”管理界面,单击“创建作业”。
  3. 在作业创建界面,配置对应作业运行参数。
    具体说明如表6所示,其他参数保持默认值即可。
    表6 Spark Jar作业参数填写

    参数名

    参数值

    所属队列

    选择已创建的DLI通用队列。例如当前选择步骤1:创建DLI通用队列创建的通用队列“sparktest”。

    Spark版本

    选择Spark版本。在下拉列表中选择支持的Spark版本,推荐使用最新版本。

    作业名称(--name)

    自定义Spark Jar作业运行的名称。当前定义为:SparkTestMeta。

    应用程序

    选择步骤6:上传Jar包到OBS和DLI下中上传到DLI程序包。例如当前选择为:“SparkJarMetadata-1.0-SNAPSHOT.jar”。

    主类

    格式为:程序包名+类名。例如当前为:com.huawei.dli.demo.DliCatalogTest。

    Spark参数(--conf)

    spark.dli.metaAccess.enable=true

    spark.sql.warehouse.dir=obs://dli-test-obs01/warehousepath

    说明:

    spark.sql.warehouse.dir参数的OBS路径为步骤2:OBS桶文件配置中配置创建。

    访问元数据

    选择:是

  4. 单击“执行”,提交该Spark Jar作业。在Spark作业管理界面显示已提交的作业运行状态。
    图13 查看作业运行状态

步骤8:查看作业运行结果

  1. 在Spark作业管理界面显示已提交的作业运行状态。初始状态显示为“启动中”。
  2. 如果作业运行成功则作业状态显示为“已成功”,通过以下操作查看创建的数据库和表。
    1. 可以在DLI控制台,左侧导航栏,单击“SQL编辑器”。在“数据库”中已显示创建的数据库“test_sparkapp”。
      图14 查看创建的数据库
    2. 双击数据库名,可以在数据库下查看已创建成功的DLI和OBS表。
      图15 查看表
    3. 双击DLI表名dli_testtable,单击“执行”查询DLI表数据。
      图16 查询DLI表数据
    4. 注释掉DLI表查询语句,双击OBS表名dli_testobstable,单击“执行”查询OBS表数据。
      图17 查询OBS表数据
  3. 如果作业运行失败则作业状态显示为“已失败”,单击“操作”列“更多”下的“Driver日志”,显示当前作业运行的日志,分析报错原因。
    图18 查看Driver日志

    原因定位解决后,可以在作业“操作”列,单击“编辑”,修改作业相关参数后,单击“执行”重新运行该作业即可。

后续指引

  • 如果您想通过Spark Jar作业访问其他数据源,请参考《使用Spark作业跨源访问数据源》。
  • 创建DLI表的语法请参考创建DLI表,创建OBS表的语法请参考创建OBS表
  • 如果是通过API接口调用提交该作业请参考以下操作说明:

    调用创建批处理作业接口,参考以下请求参数说明。

    详细的API参数说明请参考《数据湖探索API参考》>《创建批处理作业》。

    • 将请求参数中的“catalog_name”参数设置为“dli”。
    • conf 中需要增加"spark.dli.metaAccess.enable":"true"。

      如果需要执行DDL,则还要在conf中配置"spark.sql.warehouse.dir": "obs://bucket/warehousepath"。

      完整的API请求参数可以参考如下示例说明。

      {
          "queue":"citest",
          "file":"SparkJarMetadata-1.0-SNAPSHOT.jar",
          "className":"DliCatalogTest",
          "conf":{"spark.sql.warehouse.dir": "obs://bucket/warehousepath",
          "spark.dli.metaAccess.enable":"true"},
          "sc_type":"A",
          "executorCores":1,
          "numExecutors":6,
          "executorMemory":"4G",
          "driverCores":2,
          "driverMemory":"7G",
          "catalog_name": "dli"
      }

Java样例代码

本示例操作步骤采用Java进行编码,具体完整的样例代码参考如下:

package com.huawei.dli.demo;

import org.apache.spark.sql.SparkSession;

public class DliCatalogTest {
    public static void main(String[] args) {

        SparkSession spark = SparkSession
                .builder()
                .config("spark.sql.session.state.builder", "org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder")
                .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog")
                .config("spark.sql.extensions","org.apache.spark.sql.DliSparkExtension")
                .appName("java_spark_demo")
                .getOrCreate();

        spark.sql("create database if not exists test_sparkapp").collect();
        spark.sql("drop table if exists test_sparkapp.dli_testtable").collect();
        spark.sql("create table test_sparkapp.dli_testtable(id INT, name STRING)").collect();
        spark.sql("insert into test_sparkapp.dli_testtable VALUES (123,'jason')").collect();
        spark.sql("insert into test_sparkapp.dli_testtable VALUES (456,'merry')").collect();

        spark.sql("drop table if exists test_sparkapp.dli_testobstable").collect();
        spark.sql("create table test_sparkapp.dli_testobstable(age INT, name STRING) using csv options (path 'obs://dli-test-obs01/testdata.csv')").collect();


        spark.stop();

    }
}

scala样例代码

object DliCatalogTest {
  def main(args:Array[String]): Unit = {
    val sql = args(0)
    val runDdl =
Try(args(1).toBoolean).getOrElse(true)
    System.out.println(s"sql is $sql
runDdl is $runDdl")
    val sparkConf = new SparkConf(true)
    sparkConf    
      .set("spark.sql.session.state.builder","org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder")
      .set("spark.sql.catalog.class","org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") 
    sparkConf.setAppName("dlicatalogtester")

    val spark = SparkSession.builder
      .config(sparkConf)
      .enableHiveSupport()
      .config("spark.sql.extensions","org.apache.spark.sql.DliSparkExtension")
      .appName("SparkTest")
      .getOrCreate()

    System.out.println("catalog is "
+ spark.sessionState.catalog.toString)
    if (runDdl) {
      val df = spark.sql(sql).collect()
    } else {
      spark.sql(sql).show()
    }

    spark.close()
  }

}

Python样例代码

#!/usr/bin/python
# -*- coding: UTF-8 -*-

from __future__ import print_function

import sys

from pyspark.sql import SparkSession

if __name__ == "__main__":
    url = sys.argv[1]
    creatTbl = "CREATE TABLE test_sparkapp.dli_rds USING JDBC OPTIONS ('url'='jdbc:mysql://%s'," \
              "'driver'='com.mysql.jdbc.Driver','dbtable'='test.test'," \
              " 'passwdauth' = 'DatasourceRDSTest_pwd','encryption' = 'true')" % url

    spark = SparkSession \
        .builder \
        .enableHiveSupport() \
.config("spark.sql.session.state.builder","org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder") \       
.config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") \  
.config("spark.sql.extensions","org.apache.spark.sql.DliSparkExtension") \
        .appName("python Spark test catalog") \
        .getOrCreate()

    spark.sql("CREATE database if not exists test_sparkapp").collect()
    spark.sql("drop table if exists test_sparkapp.dli_rds").collect()
    spark.sql(creatTbl).collect()
    spark.sql("select * from test_sparkapp.dli_rds").show()
    spark.sql("insert into table test_sparkapp.dli_rds select 12,'aaa'").collect()
    spark.sql("select * from test_sparkapp.dli_rds").show()
    spark.sql("insert overwrite table test_sparkapp.dli_rds select 1111,'asasasa'").collect()
    spark.sql("select * from test_sparkapp.dli_rds").show()
    spark.sql("drop table test_sparkapp.dli_rds").collect()
    spark.stop()