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

此预览版引入了通过 Snowflake Horizon 目录使用外部查询引擎查询 Snowflake 托管的 Apache Iceberg™ 表的支持。为了确保与外部引擎的互操作性,`_Apache Polaris™(孵化中)`<https://github.com/apache/polaris (https://github.com/apache/polaris)> 已集成到 Horizon 目录中。此外,Horizon 目录还公开了 Apache Iceberg™ REST APIs,它允许您使用外部查询引擎读取表。

要使用外部查询引擎查询 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. 通过 Horizon 目录将外部查询引擎连接到 Iceberg 表。

  5. 查询 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.

重要

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

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

在此步骤中,您将通过 Horizon 目录将外部查询引擎连接到 Iceberg 表。此连接允许您使用外部查询引擎对表进行查询。

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

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

此步骤中的示例代码展示了如何在 Spark 中设置连接,示例代码则位于 PySpark。有关详细信息,请参阅以下主题:

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

使用以下示例代码将外部查询引擎连接到 Iceberg 表,方法是使用外部 OAuth 或密钥对身份验证:

# 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}"

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

使用以下示例代码,通过编程访问令牌 (PAT) 将外部查询引擎连接到 Iceberg 表:

# 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}"

Step 6: 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 或更早版本支持外部读取。

  • 此功能仅支持存储在 Amazon S3、Google Cloud 或 Azure 上由 Snowflake 托管的 Iceberg 表,并适用于所有公共云区域。与 S3 兼容的非 AWS 尚不支持存储。

  • 如果在表上定义了以下细粒度访问控制 (FGAC) 策略,则无法通过 Horizon Iceberg REST API 查询该 Iceberg 表:

    • 行访问策略

    • 列级安全性

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

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

语言: 中文