You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Parquet Modular Encryption读取加密Parquet文件时遇空指针错误

问题分析与解决方案

问题原因

当设置parquet.encryption.key.material.store.internally=false时,Parquet文件不会内嵌密钥材料(加密后的Data Encryption Keys,DEK),而是依赖密钥管理服务(KMS)获取解密所需的信息。但你使用的InMemoryKMS是仅用于本地测试的mock组件,它的密钥材料只存储在当前JVM的内存中:

  • 如果写入和读取是两个独立的Spark进程,读取进程的InMemoryKMS中没有写入时生成的密钥材料,就会导致解密时找不到密钥,触发NullPointerException。
  • 当store.internally=true时,密钥材料直接存储在Parquet文件内部,读取时无需依赖KMS的内存数据,因此读写操作正常。

解决方案

根据你的使用场景,提供以下三种可行方案:

方案1:单进程测试场景(适用于本地验证)

确保写入和读取代码在同一个Spark上下文(同一JVM进程)中执行,这样InMemoryKMS的内存数据可以共享,读取时能直接获取到对应的密钥材料。

完整代码示例:

public class ParquetEncryptionTest {
    public static void main(String[] args) {
        SparkSession spark = SparkSession.builder()
                .appName("ParquetEncryptionTest")
                .master("local[*]")
                .getOrCreate();

        // 配置加密参数
        spark.sparkContext().hadoopConfiguration().set("parquet.crypto.factory.class",
                "org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory");
        spark.sparkContext().hadoopConfiguration().set("parquet.encryption.key.material.store.internally", "false");
        spark.sparkContext().hadoopConfiguration().set("parquet.encryption.kms.client.class",
                "org.apache.parquet.crypto.keytools.mocks.InMemoryKMS");
        spark.sparkContext().hadoopConfiguration().set("parquet.encryption.key.list",
                "keyA:AAECAwQFBgcICQoLDA0ODw==,keyB:AAECAAECAAECAAECAAECAA==,keyC:AAECAwPFBgcICQoLDA0ODw==,keyD:AAECAwPFdgcICQoLDA0ODw==");

        // 写入加密Parquet
        String csvPath = "/home/tuantq2/Downloads/stat/perstat/stats_data_read_csv9.csv";
        Dataset<Row> csvData = spark.read()
                .format("csv")
                .option("header", "true")
                .option("inferSchema", "true")
                .load(csvPath);
        csvData.write()
                .option("parquet.encryption.column.keys", "keyA:team")
                .option("parquet.encryption.footer.key", "keyB")
                .mode(SaveMode.Overwrite)
                .parquet("hdfs://127.0.0.1:9000/data/tuantq2/parquet_sec/teampath.parquet.encrypted");

        // 读取加密Parquet(同一进程内执行)
        Dataset<Row> df = spark.read()
                .parquet("hdfs://127.0.0.1:9000/data/tuantq2/parquet_sec/teampath.parquet.encrypted/")
                .select("team");
        df.show();

        spark.stop();
    }
}

方案2:生产/跨进程场景(推荐)

替换InMemoryKMS为真实的KMS服务(如AWS KMS、Azure Key Vault、HashiCorp Vault等)。这些服务会持久化存储密钥材料,跨进程读取时可以通过官方KMS客户端获取解密所需的信息,同时提供安全的密钥生命周期管理。

需要根据所选KMS的文档配置对应的客户端参数(如访问凭证、服务端点等),并确保Spark作业具备访问KMS的权限。

方案3:保持密钥材料内嵌(最简兼容方案)

如果不需要将密钥材料存储在外部,继续使用parquet.encryption.key.material.store.internally=true即可。这种方式是Parquet加密的默认推荐模式之一,兼容绝大多数读写场景,无需额外依赖外部服务。

内容的提问来源于stack exchange,提问作者truong quoc tuan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 19:40:55