Spark全密态计算引擎高性能版使用示例

重要

本文中含有需要您注意的重要提示信息,忽略该信息可能对您的业务造成影响,请务必仔细阅读。

云原生数据仓库 AnalyticDB MySQL 版Spark全密态计算引擎高性能版,在Spark全密态引擎基础版能力的基础上,支持Parquet模块化加密功能,且兼容社区版Spark、Hadoop、Hive等计算引擎,在保证数据传输与存储过程安全的同时,提升了数据处理效率。本文介绍如何通过Spark全密态计算引擎高性能版加密数据,并基于密文表执行SQL计算。

前提条件

  • 集群的产品系列为企业版、基础版或湖仓版

  • 集群与OSS存储空间位于相同地域。

  • 已创建Job型资源组。具体操作,请参见新建资源组

  • 已创建数据库账号。

  • 已为RAM用户授予AliyunADBFullAccess、AliyunADBSparkProcessingDataRole和AnalyticDB for MySQL库表的读写权限。具体操作,请参见账号授权

数据准备

待加密的数据文件格式必须为Parquet,您可以直接下载Spark全密态示例数据,完成后续操作。

操作步骤

AnalyticDB for MySQL支持通过控制台和加密工具两种方式加密明文数据。若您的数据存储在本地,可以通过加密工具加密数据;若存储在云数据库中,可以通过控制台加密数据。两种加密方式用法的区别如下:

  • 通过控制台加密数据:上传明文数据至OSS,再加密。

  • 通过加密工具加密数据:在本地加密数据,上传密文至OSS。

通过控制台加密数据并创建密文表

  1. 数据准备章节中的明文数据上传至OSS存储空间。本文示例为oss://testBucketName/adb/Spark/customer。具体操作,请参见简单上传

  2. 登录云原生数据仓库AnalyticDB MySQL控制台,在左上角选择集群所在地域。在左侧导航栏,单击集群列表,在企业版、基础版或湖仓版页签下,单击目标集群ID。

  3. 在左侧导航栏,单击作业开发 > SQL开发

  4. SQLConsole窗口,选择Spark引擎和Job型资源组。

  5. 执行以下语句,创建密文表。

    1. 开启密态计算,设置用户的主密钥,并创建数据库。

      -- 开启native计算
      SET spark.adb.native.enabled=true;
      -- 配置资源
      SET spark.driver.resourceSpec=medium;
      SET spark.executor.instances=2;
      SET spark.executor.resourceSpec=medium;
      SET spark.app.name=Spark SQL Encryption Test;
      -- 开启密文读写支持并设置主密钥列表,KMS Client 以及 CryptoFactory(开启后引擎可同时支持明文和密文)
      SET spark.hadoop.parquet.encryption.key.list=kf:MDEyMzQ1Njc4OTAxMjM0****,kc1:bvCDwqcOJGSdZSEMLjfk****,kc2:kflI/sq+uf50Qhl1MmtG****;
      SET spark.hadoop.parquet.encryption.kms.client.class=io.glutenproject.encryption.InMemoryKMS;
      SET spark.hadoop.parquet.crypto.factory.class=org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory;
      --创建数据库
      CREATE database IF NOT EXISTS adb_external_db;

      参数说明:

      参数

      说明

      spark.hadoop.parquet.encryption.key.list

      用户主密钥列表。一个主密钥对应一个密钥ID,多个主密钥之间用半角逗号(,)分隔,每个主密钥ID与主密钥之间用半角冒号(:)分隔。格式为:<主密钥ID1>:<Base64编码的主密钥1>,<主密钥ID2>:<Base64 编码的主密钥2>。详情请参见密钥介绍

      本文示例为kf:MDEyMzQ1Njc4OTAxMjdy****,kc1:bvCDwqcOJGSdZSEMLjfk****,kc2:kflI/sq+uf50Qhl1MmtG****

      警告

      您可以使用通用工具(例如:OpenSSL)随机生成用户主密钥。用户主密钥是访问加密数据的根凭据,一旦丢失密钥,将无法再访问已有的数据,请妥善保管用户主密钥。

      spark.hadoop.parquet.encryption.kms.client.class

      KMS客户端类名。固定填写为io.glutenproject.encryption.InMemoryKMS

      spark.hadoop.parquet.crypto.factory.class

      CryptoFactory类名。固定填写为org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory

    2. 创建外表customer,用于存储明文数据。LOCATION为明文数据所在的OSS路径。本文示例为oss://testBucketName/adb/Spark/customer

      SET spark.adb.native.enabled=true;
      SET spark.hadoop.parquet.encryption.key.list=kf:MDEyMzQ1Njc4OTAxMjM0****,kc1:bvCDwqcOJGSdZSEMLjfk****,kc2:kflI/sq+uf50Qhl1MmtG****;
      SET spark.hadoop.parquet.encryption.kms.client.class=io.glutenproject.encryption.InMemoryKMS;
      SET spark.hadoop.parquet.crypto.factory.class=org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory;
      CREATE TABLE IF NOT EXISTS adb_external_db.customer 
       (
          c_custkey long,
          c_name       string,
          c_address    string,
          c_nationkey long,
          c_phone      string,
          c_acctbal    decimal(12, 2),
          c_mktsegment string,
          c_comment    string
      )
      USING parquet 
      LOCATION 'oss://testBucketName/adb/Spark/customer';
      说明
      • adb_external_db数据库中已有明文表,可跳过该步骤。

      • 若数据存储在其他云数据库中,需创建对应的外表。创建外表的语法请参见CREATE EXTERNAL TABLE

    3. 创建外表enc_customer,用于存储密文数据。本文示例将enc_customer外表的数据指定存储在oss://testBucketName/adb/Spark/enc_customer

      SET spark.adb.native.enabled=true;
      SET spark.hadoop.parquet.encryption.key.list=kf:MDEyMzQ1Njc4OTAxMjM0****,kc1:bvCDwqcOJGSdZSEMLjfk****,kc2:kflI/sq+uf50Qhl1MmtG****;
      SET spark.hadoop.parquet.encryption.kms.client.class=io.glutenproject.encryption.InMemoryKMS;
      SET spark.hadoop.parquet.crypto.factory.class=org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory;
      CREATE TABLE IF NOT EXISTS adb_external_db.enc_customer
      USING Parquet
      OPTIONS (
       'parquet.encryption.column.keys'='kc1:c_name;kc2:c_phone',
       'parquet.encryption.footer.key'='kf'
      )
      LOCATION 'oss://testBucketName/adb/Spark/enc_customer'
      AS
      SELECT *
      FROM adb_external_db.customer;

      参数说明:

      参数

      是否必填

      说明

      parquet.encryption.column.keys

      使用密钥ID所对应的主密钥加密列。一个主密钥可加密多个列,主密钥ID和列名之间用半角冒号(:)分隔,加密列之间用半角逗号(,)分隔,不同主密钥之间用半角分号(;)分隔。

      parquet.encryption.footer.key

      Footer密钥,用来加密Parquet文件的元数据等信息。

      说明

      Footer是位于Parquet文件尾部的数据结构,一般用来存储文件的元数据信息,例如:版本号、分组元信息、列的元信息以及密钥元信息等。

      重要

      parquet.encryption.column.keysparquet.encryption.footer.key参数必须同时设置,否则文件不会被加密。

    4. (可选)删除外表customer

      DROP TABLE IF EXISTS adb_external_db.customer;
      重要

      DROP TABLE语句会删除customer外表,对应OSS中的元数据请手动删除,避免明文数据泄露。

  6. 创建外表enc_customer_output,将enc_customer表的SQL计算结果写入enc_customer_output外表。enc_customer_output外表的数据指定存储在oss://testBucketName/adb/Spark/enc_customer_output

    SET spark.adb.native.enabled=true;
    SET spark.hadoop.parquet.encryption.key.list=kf:MDEyMzQ1Njc4OTAxMjM0****,kc1:bvCDwqcOJGSdZSEMLjfk****,kc2:kflI/sq+uf50Qhl1MmtG****;
    SET spark.hadoop.parquet.encryption.kms.client.class=io.glutenproject.encryption.InMemoryKMS;
    SET spark.hadoop.parquet.crypto.factory.class=org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory;
    CREATE TABLE IF NOT EXISTS adb_external_db.enc_customer_output
    USING Parquet
    OPTIONS (
     'parquet.encryption.column.keys'='kc1:c_name;kc2:c_phone',
     'parquet.encryption.footer.key'='kf'
    )
    LOCATION 'oss://testBucketName/adb/Spark/enc_customer_output'
    AS
    SELECT *
    FROM adb_external_db.enc_customer
    WHERE 
    c_custkey < 15;
  7. 解密计算结果。

    1. 创建外表customer_output,将enc_customer_output表的数据解密后写入customer_output外表。customer_output外表的数据指定存储在oss://testBucketName/adb/Spark/customer_output

      SET spark.adb.native.enabled=true;
      SET spark.hadoop.parquet.encryption.key.list=kf:MDEyMzQ1Njc4OTAxMjM0****,kc1:bvCDwqcOJGSdZSEMLjfk****,kc2:kflI/sq+uf50Qhl1MmtG****;
      SET spark.hadoop.parquet.encryption.kms.client.class=io.glutenproject.encryption.InMemoryKMS;
      SET spark.hadoop.parquet.crypto.factory.class=org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory;
      CREATE TABLE IF NOT EXISTS adb_external_db.customer_output
      USING Parquet
      LOCATION 'oss://testBucketName/adb/Spark/customer_output'
      AS
      SELECT *
      FROM adb_external_db.enc_customer_output;
    2. 查询customer_output表数据。

      SELECT * FROM adb_external_db.customer_output;

通过加密工具加密数据并创建密文表

  1. 通过加密工具将保存在本地的明文数据加密成密文数据集,加密工具的更多信息,请参见Spark加密工具

    import org.apache.spark.sql.SparkSession
    import org.apache.spark.sql.functions._
    import org.apache.spark.SparkConf
    
    // 初始化SparkSession,并输入加解密相关的参数。
    val conf = new SparkConf()
    .set("spark.hadoop.parquet.encryption.kms.client.class", "org.apache.parquet.crypto.keytools.mocks.InMemoryKMS")
    .set("spark.hadoop.parquet.encryption.key.list", "kf:MDEyMzQ1Njc4OTAxMjM0****,kc1:bvCDwqcOJGSdZSEMLjfk****,kc2:kflI/sq+uf50Qhl1MmtG****")
    .set("spark.hadoop.parquet.crypto.factory.class", "org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory")
    
    val spark = SparkSession.builder().appName("SquareDataFrame").config(conf).getOrCreate()
    
    // 读取明文customer。
    val df = spark.read.parquet("customer")
    // 对明文customer加密,其中name列使用kc1加密,footer使用kf加密,加密后的密文文件为enc_customer。
    df.write
    .option("parquet.encryption.column.keys" , "kc1:c_name")
    .option("parquet.encryption.footer.key" , "kf")
    // 密文数据集所在的本地路径。
    .parquet("enc_customer")

    参数说明:

    参数

    是否必填

    说明

    spark.hadoop.parquet.encryption.kms.client.class

    KMS客户端类名。

    • 本地加密时需填写为org.apache.parquet.crypto.keytools.mocks.InMemoryKMS

    • 控制台创建密文表时需填写为io.glutenproject.encryption.InMemoryKMS

    spark.hadoop.parquet.encryption.key.list

    用户主密钥列表。一个主密钥对应一个密钥ID,多个主密钥之间用半角逗号(,)分隔,每个主密钥ID与主密钥之间用半角冒号(:)分隔。格式为:<主密钥ID1>:<Base64编码的主密钥1>,<主密钥ID2>:<Base64 编码的主密钥2>。详情请参见密钥介绍

    本文示例为kf:MDEyMzQ1Njc4OTAxMjdy****,kc1:bvCDwqcOJGSdZSEMLjfk****,kc2:kflI/sq+uf50Qhl1MmtG****

    警告

    您可以使用通用工具(例如:OpenSSL)随机生成用户主密钥。用户主密钥是访问加密数据的根凭据,一旦丢失密钥,将无法再访问已有的数据,请妥善保管用户主密钥。

    spark.hadoop.parquet.crypto.factory.class

    CryptoFactory类名。固定填写为org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory

    parquet.encryption.column.keys

    使用密钥ID所对应的主密钥加密列。一个主密钥可加密多个列,主密钥ID和列名之间用半角冒号(:)分隔,加密列之间用半角逗号(,)分隔,不同主密钥之间用半角分号(;)分隔。

    parquet.encryption.footer.key

    Footer密钥,用来加密Parquet文件的元数据等信息。

    说明

    Footer是位于Parquet文件尾部的数据结构,一般用来存储文件的元数据信息,例如:版本号、分组元信息、列的元信息以及密钥元信息等。

  2. 将密文数据集enc_customer.parquet上传至OSS。本文示例为oss://testBucketName/adb/Spark/enc_customer.parquet。具体操作,请参见简单上传

  3. 创建密文表。

    1. 登录云原生数据仓库AnalyticDB MySQL控制台,在左上角选择集群所在地域。在左侧导航栏,单击集群列表,在企业版、基础版或湖仓版页签下,单击目标集群ID。

    2. 在左侧导航栏,单击作业开发 > SQL开发

    3. SQLConsole窗口,选择Spark引擎和Job型资源组。

    4. 执行以下语句,创建密文表。

      1. 开启native计算,并创建数据库。

        -- 开启native计算
        SET spark.adb.native.enabled=true;
        -- 配置资源
        SET spark.driver.resourceSpec=medium;
        SET spark.executor.instances=2;
        SET spark.executor.resourceSpec=medium;
        -- 开启密文读写支持并设置主密钥列表,KMS Client 以及 CryptoFactory(开启后引擎可同时支持明文和密文)
        SET spark.hadoop.parquet.encryption.key.list=kf:MDEyMzQ1Njc4OTAxMjM0****,kc1:bvCDwqcOJGSdZSEMLjfk****,kc2:kflI/sq+uf50Qhl1MmtG****;
        SET spark.hadoop.parquet.encryption.kms.client.class=io.glutenproject.encryption.InMemoryKMS;
        SET spark.hadoop.parquet.crypto.factory.class=org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory;
        -- 创建数据库
        CREATE DATABASE IF NOT EXISTS adb_external_db;
      2. 创建外表enc_customerLOCATION为密文数据集enc_customer所在的OSS路径。本文示例为oss://testBucketName/adb/Spark/enc_customer.parquet

        SET spark.adb.native.enabled=true;
        SET spark.hadoop.parquet.encryption.key.list=kf:MDEyMzQ1Njc4OTAxMjM0****,kc1:bvCDwqcOJGSdZSEMLjfk****,kc2:kflI/sq+uf50Qhl1MmtG****;
        SET spark.hadoop.parquet.encryption.kms.client.class=io.glutenproject.encryption.InMemoryKMS;
        SET spark.hadoop.parquet.crypto.factory.class=org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory;
        CREATE TABLE IF NOT EXISTS adb_external_db.enc_customer 
        USING parquet
        LOCATION  'oss://testBucketName/adb/Spark/enc_customer';
  4. 创建外表enc_customer_output,将enc_customer表的SQL计算结果写入enc_customer_output外表。enc_customer_output外表的数据指定存储在oss://testBucketName/adb/Spark/enc_customer_output

    SET spark.adb.native.enabled=true;
    SET spark.hadoop.parquet.encryption.key.list=kf:MDEyMzQ1Njc4OTAxMjM0****,kc1:bvCDwqcOJGSdZSEMLjfk****,kc2:kflI/sq+uf50Qhl1MmtG****;
    SET spark.hadoop.parquet.encryption.kms.client.class=io.glutenproject.encryption.InMemoryKMS;
    SET spark.hadoop.parquet.crypto.factory.class=org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory;
    CREATE TABLE IF NOT EXISTS adb_external_db.enc_customer_output
    USING Parquet
    OPTIONS (
     'parquet.encryption.column.keys'='kc1:c_name;kc2:c_phone',
     'parquet.encryption.footer.key'='kf'
    )
    LOCATION 'oss://testBucketName/adb/Spark/enc_customer_output'
    AS
    SELECT *
    FROM adb_external_db.enc_customer
    WHERE 
    c_custkey < 15;
  5. 下载密文结果并解密。

    1. 从OSS路径oss://testBucketName/adb/Spark/enc_customer_output下载密文计算结果到本地。具体操作,请参见下载文件

    2. 解密计算结果密文数据集,并将解密后的文件保存在customer_output中。

      // 解密密文数据集
      val conf = new SparkConf()
      .set("spark.hadoop.parquet.encryption.kms.client.class", "org.apache.parquet.crypto.keytools.mocks.InMemoryKMS")
      .set("spark.hadoop.parquet.encryption.key.list", "kf:MDEyMzQ1Njc4OTAxMjM0****,kc1:bvCDwqcOJGSdZSEMLjfk****,kc2:kflI/sq+uf50Qhl1MmtG****")
      .set("spark.hadoop.parquet.crypto.factory.class", "org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory")
      val spark = SparkSession.builder().appName("SquareDataFrame").config(conf).getOrCreate()
      val df2 = spark.read.parquet("enc_customer_output")
      // 将解密后的文件下载至本地
      df2.write
      .parquet("customer_output")