通过 Snowflake Horizon 目录使用外部引擎查询 Apache Iceberg™ 表

This preview introduces support for querying Snowflake-managed Apache Iceberg™ tables by using an external query engine through Snowflake Horizon Catalog. To ensure this interoperability with external engines, Apache Polaris™ (incubating) (https://github.com/apache/polaris) is integrated into Horizon Catalog. In addition, Horizon Catalog exposes Apache Iceberg™ REST APIs. These APIs let you read the tables by using external query engines.

要使用外部查询引擎查询 Snowflake 托管的 Iceberg 表,您可以使用此功能,而不是 将 Sguowflake 托管的 Iceberg 表与 Snowflake Open Catalog 同步。有关 Open Catalog 的更多信息,请参阅 Snowflake Open Catalog 概述

通过 Horizon 目录将外部查询引擎连接到 Iceberg 表,您可以执行以下任务:

  • 使用任何支持开放 Iceberg REST 协议的外部查询引擎(例如 Apache Spark™)查询这些表。

  • 使用单个 Horizon 目录端点,查询新的或现有的 Snowflake 账户中任何现有和新的 Snowflake 托管的 Iceberg 表。

  • 使用 Snowflake 中的现有用户、角色、策略和身份验证来查询表。

  • 使用分发的凭据。

有关 Snowflake Horizon 目录的更多信息,请参阅 Snowflake Horizon 目录

下图显示了外部查询引擎通过 Horizon 目录读取 Snowflake 托管的 Iceberg 表,以及 Snowflake 对这些表的读取和写入:

该图显示了外部查询引擎通过 Horizon 目录读取 Snowflake 托管的 Iceberg 表,以及 Snowflake 读取和 写入这些表。

计费

  • Horizon Iceberg REST 目录 API 适用于所有 Snowflake 版本。

  • API 请求按每百万次调用 0.5 Credit 计费,并作为云服务进行收费。

  • 对于跨区域数据访问,将适用 Snowflake 服务消耗表 中的标准跨区域数据出口费。

备注

在此功能正式发布之前,客户不会收到任何账单。

准备工作

检索包含要查询的 Iceberg 表的 Snowflake 的账户账户标识符。有关说明,请参阅 账户标识符。您可以在 将外部查询引擎连接到 Iceberg 表 时指定此标识符。

小技巧

要获取您的账户标识符,请使用 SQL,您可以运行以下命令:

SELECT CURRENT_ORGANIZATION_NAME() || '-' || CURRENT_ACCOUNT_NAME();
Copy

专用连接(可选)

为确保安全连接,建议在访问 Horizon Catalog 端点时,为您的 Snowflake 账户配置 入站出站 专用连接。

备注

专用连接仅支持存储在 Amazon S3 或 Azure 存储 (ADLS) 中的由 Snowflake 管理的 Iceberg 表。

使用外部查询引擎查询 Iceberg 表的工作流程

要使用外部查询引擎查询 Iceberg 表,请完成以下步骤:

  1. 创建 Iceberg 表。

  2. 配置访问控制。

  3. 获取用于身份验证的访问令牌。

  4. Verify access token permissions.

  5. (可选)配置数据保护策略。

  6. 通过 Horizon 目录将外部查询引擎连接到 Iceberg 表。

  7. 查询 Iceberg 表。

第 1 步:创建 Iceberg 表

重要

如果您已经拥有要查询的 Snowflake 托管的 Iceberg 表,则可以跳过此步骤。

在此步骤中,您将创建 Snowflake 托管的 Iceberg 表,这些表使用 Snowflake 作为目录,从而支持通过外部查询引擎对这些表进行查询。有关说明,请参阅以下主题:

第 2 步:配置访问控制

重要

  • 通过 Horizon 目录端点访问 Iceberg 表时,不支持角色名称中包含连字符 (-) 的 Snowflake 角色。

  • 如果您已经配置具备访问目标 Iceberg 表权限的角色,则可以跳过此步骤。

在此步骤中,您将为要使用外部查询引擎查询的 Snowflake 托管的 Iceberg 表配置访问控制。例如,您可以在 Snowflake 中设置以下角色:

  • DATA_ENGINEER 角色,该角色有权访问数据库中的所有架构和所有 Snowflake 托管的 Iceberg 表。

  • DATA_ANALYST 角色,该角色可以访问数据库中的一个架构,但只能访问该架构内的两个 Snowflake 托管的 Iceberg 表。

有关说明,请参阅 配置访问控制。有关 Snowflake 中访问控制的详细信息,请参阅 访问控制概述

第 3 步:获取用于身份验证的访问令牌

在此步骤中,您将获得一个访问令牌,您必须使用该令牌对 Snowflake 账户的 Horizon 目录端点进行身份验证。您需要为配置为可以访问 Snowflake 托管的 Iceberg 表的每个用户(服务或人员)和角色获取访问令牌。例如,您需要为具有 DATA_ENGINEER 角色的用户获取一个访问令牌,并为具有另一个 DATA_ANALYST 角色的用户再获取一个访问令牌。

You specify this access token later when you connect an external query engine to Iceberg tables through Horizon Catalog.

您可以使用以下身份验证选项之一获取访问令牌:

External OAuth

If you're using External OAuth, generate an access token for your identity provider. For instructions, see External OAuth 概览.

备注

对于外部 OAuth,您还可以使用自动令牌刷新配置与引擎的连接,而不是指定访问令牌。

密钥对身份验证

If you use key-pair authentication, to obtain an access token, you sign a JSON web token (JWT) with your private key.

The following steps cover how to generate an access token for key-pair authentication:

  1. Configure key-pair authentication

  2. Grant a role to the user

  3. Generate a JSON Web Token (JWT)

  4. Generate an access token

第 1 步:配置密钥对身份验证

在此步骤中,您需要执行以下任务:

  • 生成私钥

  • 生成公钥

  • 安全地存储私钥和公​​钥

  • 授予向 Snowflake 用户分配公钥的权限

  • 将公钥分配给 Snowflake 用户

  • 验证用户的公钥指纹

有关说明,请参阅 配置密钥对身份验证

第 2 步:向用户授予角色

运行 GRANT ROLE 命令,将具有查询目标表权限的 Snowflake 角色授予密钥对身份验证用户。例如,要向 my_service_user 用户授予 ENGINEER 角色,请运行以下命令:

GRANT ROLE ENGINEER to user my_service_user;
Copy

第 3 步:生成 JSON Web 令牌 (JWT)

在此步骤中,您将使用 SnowSQL 生成用于密钥对身份验证的 JSON Web 令牌 (JWT)。

备注

使用 SnowSQL 生成 JWT:

snowsql --private-key-path "<private_key_file>" \
  --generate-jwt \
  -h "<account_identifier>.snowflakecomputing.cn" \
  -a "<account_locator>" \
  -u "<user_name>"
Copy

其中:

  • <private_key_file> is the path to your private key file that corresponds to the public key assigned to your Snowflake user. For example: /Users/jsmith/.ssh/rsa_key.p8.

  • <account_identifier> is the account identifier for your Snowflake account, in the format <organization_name>-<account_name>. To find the account identifier, see 准备工作. An example of an account identifier is myorg-myaccount.

  • <account_locator> 是 Snowflake 账户的账户定位器。

    要查找您的账户定位器,请参阅 在 Snowsight 中查找您的 Snowflake 账户信息,并并在 Account Details 对话框中查看 账户定位器

  • <user_name> 是 Snowflake 用户的用户名,且该用户已获分配公钥。

Step 4: Generate an access token

重要

要生成访问令牌,您必须先 生成一个 JWT。之所以需要先生成 JWT,是因为访问令牌正是基于 JWT 生成的。

使用命令 curl 生成访问令牌:

curl -i --fail -X POST "https://<account_identifier>.snowflakecomputing.cn/polaris/api/catalog/v1/oauth/tokens" \
 --header 'Content-Type: application/x-www-form-urlencoded' \
 --data-urlencode 'grant_type=client_credentials' \
 --data-urlencode 'scope=session:role:<role>' \
 --data-urlencode 'client_secret=<JWT_token>'
Copy

其中:

  • <account_identifier> is the account identifier for your Snowflake account, in the format <organization_name>-<account_name>. To find the account identifier, see 准备工作. An example of an account identifier is myorg-myaccount.

  • <role> is the Snowflake role that is granted access to Iceberg tables, such as ENGINEER.

  • <JWT_token> 是您在上一步中生成的 JWT。

Programmatic access token (PAT)

If you use PATs, generate a PAT for authentication.

First, you generate a PAT, which you use to connect an external query engine to Iceberg tables. Then, you generate an access token, which you only use to verify the permissions for your PAT.

第 1 步:生成 PAT

有关如何配置和生成 PAT,请参阅 用于身份验证的编程访问令牌

Step 2: Generate an access token for your PAT

在此步骤中,将为 PAT 生成访问令牌。

注意

仅在为 PAT 验证权限 时,需提供本步骤生成的访问令牌。将外部查询引擎连接至 Iceberg 表 时,必须指定上一步骤生成的 PAT,而非本步骤生成的访问令牌。

使用 curl 命令为 PAT 生成访问令牌:

curl -i --fail -X POST "https://<account_identifier>.snowflakecomputing.cn/polaris/api/catalog/v1/oauth/tokens" \
 --header 'Content-Type: application/x-www-form-urlencoded' \
 --data-urlencode 'grant_type=client_credentials' \
 --data-urlencode 'scope=session:role:<role>' \
 --data-urlencode 'client_secret=<PAT_token>'
Copy

其中:

  • <account_identifier> is the account identifier for your Snowflake account, in the format <organization_name>-<account_name>. To find the account identifier, see 准备工作. An example of an account identifier is myorg-myaccount.

  • <role> is the Snowflake role that is granted to your PAT and has access to the Iceberg tables you want to query, such as ENGINEER.

  • <PAT_token> 是您在上一步骤生成的 PAT 值。

Step 4: Verify access token permissions

在此步骤中,您将验证在上一步中获取的访问令牌的权限。

验证对 Horizon IRC 端点的访问权限

使用 curl 命令验证您是否具有访问 Horizon IRC 端点的权限:

curl -i --fail -X GET "https://<account_identifier>.snowflakecomputing.cn/polaris/api/catalog/v1/config?warehouse=<database_name>" \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json"
Copy

其中:

  • <account_identifier> is the account identifier for your Snowflake account, in the format <organization_name>-<account_name>. To find the account identifier, see 准备工作. An example of an account identifier is myorg-myaccount.

  • <access_token> is your access token that you generated. If you're using a PAT, this value is the access token you generated, not the personal access token (PAT) you generated.

  • <database_name> 是要查询的数据库的名称。

    重要

    您必须以 全部大写字母 指定数据库名称,即使它是使用小写字母创建的。

返回值示例:

{
  "defaults": {
    "default-base-location": ""
  },
  "overrides": {
    "prefix": "MY-DATABASE"
  }
}

检索表的元数据

您还可以通过 GET 请求检索表的元数据。Snowflake 使用 loadTable (https://github.com/apache/iceberg/blob/apache-iceberg-1.6.1/open-api/rest-catalog-open-api.yaml#L616) 操作从您的 REST 目录加载表元数据。

curl -i --fail -X GET "https://<account_identifier>.snowflakecomputing.cn/polaris/api/catalog/v1/<database_name>/namespaces/<namespace_name>/tables/<table_name>" \
 -H "Authorization: Bearer <access_token>" \
 -H "Content-Type: application/json"
Copy

其中:

  • <account_identifier> is the account identifier for your Snowflake account, in the format <organization_name>-<account_name>. To find the account identifier, see 准备工作. An example of an account identifier is myorg-myaccount.

  • <database_name> 是要检索其元数据的表的数据库。

  • <namespace_name> 是要检索其元数据的表的命名空间。

  • <table_name> 是要检索其元数据的表。

  • <access_token> is your access token that you generated. If you're using a PAT, this value is the access token you generated, not the personal access token (PAT) you generated.

重要

您必须使用 全部大写字母 指定数据库、命名空间和表名称,即使对象是使用小写字母创建的。

(可选)第 5 步:配置数据保护策略

在此步骤中,为 Iceberg 表配置数据保护策略。如果没有需要使用 Snowflake 数据策略保护的表,可以继续执行下一步。

备注

受数据保护策略保护的表可通过 Horizon Iceberg RESTAPI 或使用 Apache Spark™ 访问。

有关配置数据保护策略的说明,请参阅 在通过 Horizon Iceberg REST API 访问的 Iceberg 表上配置数据保护策略并使用 Apache Spark™

Step 6: Connect an external query engine to Iceberg tables through Horizon Catalog

In this step, you connect an external query engine to Iceberg tables through Horizon Catalog. With this connection, you can query the tables by using the external query engine.

外部引擎使用 Snowflake 公开的 Apache Iceberg™ REST 端点。对于您的 Snowflake 账户,此端点采用以下格式:

https://<account_identifier>.snowflakecomputing.cn/polaris/api/catalog
Copy

The example code in this step shows how to set up a connection in Spark, and the example code is in PySpark. For more information, see the following sections:

使用外部 OAuth 或密钥对身份验证进行连接

使用以下配置之一进行连接:

Connect an external query engine without enforcing data policies

  • To connect the external query engine to Iceberg tables by using External OAuth or key pair authentication. Use the following example code.

此代码不强制执行数据保护策略:

# Snowflake Horizon Catalog Configuration, change as per your environment

CATALOG_URI = "https://<account_identifier>.snowflakecomputing.cn/polaris/api/catalog"
HORIZON_SESSION_ROLE = f"session:role:<role>"
CATALOG_NAME = "<database_name>" #provide in UPPER CASE

# Cloud Service Provider Region Configuration (where the Iceberg data is stored)
REGION = "eastus2"

# Paste the External Oauth Access token that you generated in Snowflake here
ACCESS_TOKEN = "<your_access_token>"

# Iceberg Version
ICEBERG_VERSION = "1.9.1"

def create_spark_session():
  """Create and configure Spark session for Snowflake Iceberg access."""
  spark = (
      SparkSession.builder
      .appName("SnowflakeIcebergReader")
      .master("local[*]")

# JAR Dependencies for Iceberg and Azure
      .config(
          "spark.jars.packages",
          f"org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:{ICEBERG_VERSION},"
          f"org.apache.iceberg:iceberg-aws-bundle:{ICEBERG_VERSION}"
          # for Azure storage, use the below package and comment above azure bundle
          # f"org.apache.iceberg:iceberg-azure-bundle:{ICEBERG_VERSION}"
      )

      # Iceberg SQL Extensions
      .config("spark.sql.extensions", "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions")
      .config("spark.sql.defaultCatalog", CATALOG_NAME)

      # Horizon REST Catalog Configuration
      .config(f"spark.sql.catalog.{CATALOG_NAME}", "org.apache.iceberg.spark.SparkCatalog")
      .config(f"spark.sql.catalog.{CATALOG_NAME}.type", "rest")
      .config(f"spark.sql.catalog.{CATALOG_NAME}.uri", CATALOG_URI)
      .config(f"spark.sql.catalog.{CATALOG_NAME}.warehouse", CATALOG_NAME)
      .config(f"spark.sql.catalog.{CATALOG_NAME}.token", ACCESS_TOKEN)
      .config(f"spark.sql.catalog.{CATALOG_NAME}.scope", HORIZON_SESSION_ROLE)
      .config(f"spark.sql.catalog.{CATALOG_NAME}.client.region", REGION)

      # Required for vended credentials
      .config(f"spark.sql.catalog.{CATALOG_NAME}.header.X-Iceberg-Access-Delegation", "vended-credentials")
      .config("spark.sql.iceberg.vectorization.enabled", "false")
      .getOrCreate()
  )
  spark.sparkContext.setLogLevel("ERROR")
  return spark
Copy

其中:

  • <account_identifier> is your Snowflake account identifier for the Snowflake account that contains the Iceberg tables that you want to query. To find this identifier, see 准备工作.

  • <your_access_token> 是您获得的访问令牌。要获取该令牌,请参阅 第 3 步:获取用于身份验证的访问令牌

    备注

    对于外部 OAuth,您还可以使用自动令牌刷新配置与引擎的连接,而不是指定访问令牌。

  • <database_name> 是 Snowflake 账户中数据库的名称,该数据库包含您要查询的 Snowflake 托管的 Iceberg 表。

    备注

    Spark 中的 .warehouse 属性需要 Snowflake 数据库 名称,而不是 Snowflake 仓库名称。

  • <role> 是 Snowflake 中配置为对要查询的 Iceberg 表具有访问权限的角色。例如:DATA_ENGINEER。

重要

默认情况下,代码示例是针对存储在 Amazon S3 上的 Apache Iceberg™ 表进行设置的。如果您的 Iceberg 表存储在 Azure 存储 (ADLS) 中,请执行以下步骤:

  1. 注释以下行:f"org.apache.iceberg:iceberg-aws-bundle:{ICEBERG_VERSION}"

  2. 取消注释以下行:# f"org.apache.iceberg:iceberg-azure-bundle:{ICEBERG_VERSION}"

Connect an external query engine with data policies enforced

使用编程访问令牌 (PAT) 进行连接

使用以下配置之一进行连接:

Connect an external query engine without enforcing data policies

  • To connect the external query engine to Iceberg tables by using a programmatic access token (PAT), use the following example code.

此代码不强制执行数据保护策略:

# Snowflake Horizon Catalog Configuration, change as per your environment

CATALOG_URI = "https://<account_identifier>.snowflakecomputing.cn/polaris/api/catalog"
HORIZON_SESSION_ROLE = f"session:role:<role>"
CATALOG_NAME = "<database_name>" #provide in UPPER CASE

# Cloud Service Provider Region Configuration (where the Iceberg data is stored)
REGION = "eastus2"

# Paste the PAT you generated in Snowflake here
PAT_TOKEN = "<your_PAT_token>"

# Iceberg Version
ICEBERG_VERSION = "1.9.1"

def create_spark_session():
  """Create and configure Spark session for Snowflake Iceberg access."""
  spark = (
      SparkSession.builder
      .appName("SnowflakeIcebergReader")
      .master("local[*]")

# JAR Dependencies for Iceberg and Azure
      .config(
          "spark.jars.packages",
          f"org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:{ICEBERG_VERSION},"
          f"org.apache.iceberg:iceberg-aws-bundle:{ICEBERG_VERSION}"
          # for Azure storage, use the below package and comment above azure bundle
          # f"org.apache.iceberg:iceberg-azure-bundle:{ICEBERG_VERSION}"
      )

      # Iceberg SQL Extensions
      .config("spark.sql.extensions", "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions")
      .config("spark.sql.defaultCatalog", CATALOG_NAME)

      # Horizon REST Catalog Configuration
      .config(f"spark.sql.catalog.{CATALOG_NAME}", "org.apache.iceberg.spark.SparkCatalog")
      .config(f"spark.sql.catalog.{CATALOG_NAME}.type", "rest")
      .config(f"spark.sql.catalog.{CATALOG_NAME}.uri", CATALOG_URI)
      .config(f"spark.sql.catalog.{CATALOG_NAME}.warehouse", CATALOG_NAME)
      .config(f"spark.sql.catalog.{CATALOG_NAME}.credential", PAT_TOKEN)
      .config(f"spark.sql.catalog.{CATALOG_NAME}.scope", HORIZON_SESSION_ROLE)
      .config(f"spark.sql.catalog.{CATALOG_NAME}.client.region", REGION)

      # Required for vended credentials
      .config(f"spark.sql.catalog.{CATALOG_NAME}.header.X-Iceberg-Access-Delegation", "vended-credentials")
      .config("spark.sql.iceberg.vectorization.enabled", "false")
      .getOrCreate()
  )
  spark.sparkContext.setLogLevel("ERROR")
  return spark
Copy

其中:

  • <account_identifier> is your Snowflake account identifier for the Snowflake account that contains the Iceberg tables that you want to query. To find this identifier, see 准备工作.

  • <your_PAT_token> 是您获取的 PAT。要获取该令牌,请参阅 第 3 步:获取用于身份验证的访问令牌

  • <role> 是 Snowflake 中配置为对要查询的 Iceberg 表具有访问权限的角色。例如:DATA_ENGINEER。

  • <database_name> 是 Snowflake 账户中数据库的名称,该数据库包含您要查询的 Snowflake 托管的 Iceberg 表。

    备注

    Spark 中的 .warehouse 属性需要 Snowflake 数据库 名称,而不是 Snowflake 仓库名称。

重要

默认情况下,代码示例是针对存储在 Amazon S3 上的 Apache Iceberg™ 表进行设置的。如果您的 Iceberg 表存储在 Azure 存储 (ADLS) 中,请执行以下步骤:

  1. 注释以下行:f"org.apache.iceberg:iceberg-aws-bundle:{ICEBERG_VERSION}"

  2. 取消注释以下行:# f"org.apache.iceberg:iceberg-azure-bundle:{ICEBERG_VERSION}"

Connect an external query engine with data policies enforced

Step 7: Query Iceberg tables

此步骤提供了使用 Apache Spark™ 查询 Iceberg 表的以下代码示例:

  • 显示命名空间

  • 使用命名空间

  • 显示表

  • 查询表

显示命名空间

spark.sql("show namespaces").show()
Copy

使用命名空间

spark.sql("use namespace <your_schema_name_in_snowflake>")
Copy

显示表

spark.sql("show tables").show()
Copy

查询表

spark.sql("use namespace spark_demo")
spark.sql("select * from <your_table_name_in_snowflake>").show()
Copy

使用外部查询引擎查询 Iceberg 表的注意事项

使用外部查询引擎查询 Iceberg 表时,请考虑以下事项:

  • 对于 Snowflake 中的表:

    • 仅支持 Snowflake 托管的 Iceberg 表。

    • 不支持查询远程或外部托管的 Iceberg 表,包括 Delta Direct 和 Parquet Direct 表以及 Snowflake 原生表。

  • 您可以查询,但不能写入 Iceberg 表。

  • 仅 Iceberg 版本 2 或更早版本支持外部读取。

  • This feature is only supported for Snowflake-managed Iceberg tables that are stored on Amazon S3, Google Cloud, or Microsoft Azure for all public cloud regions. S3-compatible non-AWS storage is not yet supported.

  • 受以下细粒度数据策略保护的表,可以通过 Horizon Iceberg REST API 访问,并使用 Apache Spark™:

    • Masking policies

    • 基于标签的掩码策略

    • 行访问策略

  • 通过 Horizon 目录端点访问 Iceberg 表时,不支持角色名称中包含连字符 (-) 的 Snowflake 角色。

  • 不支持显式授予 Horizon 目录端点对存储账户的访问权限。我们建议使用专用连接,以确保从外部引擎到 Horizon 目录,以及从 Horizon 目录到存储账户的连接安全。

语言: 中文