通过托管 UDF 将 Python 的强大功能引入 BigQuery

1. 简介

结构化查询语言 (SQL) 是数据仓库分析的行业标准。但是,用纯 SQL 表达复杂的程序逻辑、数学计算、文本清理或机器学习准备工作流可能非常具有挑战性。

过去,数据团队在需要复杂的自定义 Python 处理时,会从 BigQuery 中提取大量数据集,在外部自定义虚拟机或集群中处理这些数据集,然后将结果加载回来。这种方法会引入较高的网络延迟,通过移动数据来增加合规性风险,并产生基础架构管理开销。

BigQuery 托管式 Python 用户定义函数 (UDF) 通过在无服务器资源上运行自定义代码来解决这些问题,这些资源会自动扩缩到数百万行。Google Cloud 负责管理编译、映像构建、安全补丁和执行,让您可以直接在数据所在的位置运行自定义计算。

在此 Codelab 中,您将基于 StackOverflow 社区数据构建分析和文本预处理流水线,为下游报告和机器学习做好准备。

前提条件

  • 启用了结算功能的 Google Cloud 项目。
  • 对 SQL、IAM 和 BigQuery 概念有基本的了解。

学习内容

  • 如何在公共数据集上调用预编译的公共 Python UDF 来分析数据分布。
  • 如何使用 beautifulsoup4 部署自己的自定义 Python UDF 来清理非结构化数据。
  • 如何配置 BigQuery Cloud 资源连接,以安全地下载机器学习资产,并使用内存中的容器缓存通过 Hugging Face transformers 库执行本地机器学习分词。
  • 如何将这些步骤链接到单个高性能 SQL 流水线中。

2. 设置和要求

启动 Cloud Shell

虽然可以通过笔记本电脑对 Google Cloud 进行远程操作,但在此 Codelab 中,您将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。

  1. 前往 Google Cloud 控制台,然后 选择或创建 Google Cloud 项目
  2. ⚠️ 请记下项目 ID。您将在整个实验中使用它。

39b6a5563d69ccfb.png

  1. 在新标签页中打开 Cloud Shell: https://shell.cloud.google.com/
  2. 如果出现提示,点击授权。
  3. 替换 PROJECT_ID,并将以下命令粘贴到终端中:
cat << 'EOF' > env.sh
#!/bin/bash
# env.sh: Environment variables for BigQuery Python UDFs codelab

# ⚠️ Replace 'YOUR_PROJECT_ID' with your actual Google Cloud Project ID
export PROJECT_ID="YOUR_PROJECT_ID"
export REGION="us"
export BQ_DATASET="python_udfs"
export BQ_RESOURCE_CONN="external_api_connection"
EOF

将变量应用于活跃会话:

source ./env.sh

启用 API 并创建 BigQuery 数据集

在项目中启用必要的 Google Cloud 服务,并创建目标数据集:

# Enable API Services
gcloud services enable \
  bigquery.googleapis.com \
  bigqueryconnection.googleapis.com --quiet

# Create BigQuery Dataset
bq mk --location=${REGION} --dataset ${PROJECT_ID}:${BQ_DATASET}

3. 使用公共 Python UDF 探索数据分布

在部署自定义代码之前,探索数据集并过滤掉低质量的噪声非常有用。在此步骤中,您将分析 StackOverflow 问题,以查找活跃用户并了解其问题得分的统计分布。

为什么使用 Python UDF 来执行此操作?

在纯 SQL 中,对分组的数据数组计算多个精确的百分位(例如第 25 个、第 50 个、第 75 个和第 95 个百分位)非常复杂且资源密集。标准 SQL 分析函数(如 PERCENTILE_CONT)需要的是扁平的行,而不是嵌套数组。如需计算每行预聚合数组的精确百分位,您必须编写详细的子查询,这些子查询会针对每个百分位指标进行取消嵌套、排序和重新聚合,效率低下。

通过在 UDF 中使用 NumPy(Python 的高度优化的科学库),您可以使用一行代码计算数字数组的精确数学百分位。

执行

Google Cloud 托管了多个 预编译的公共 UDF(点击 例程 标签页)。由于 BigQuery 需要显式类型匹配,我们将使用通用表表达式 (CTE) 来预聚合数据,并使用 UNNEST 表达式将整数数组转换为浮点数组。

BigQuery Studio 控制台 中运行以下查询:

WITH raw_user_scores AS (
  -- 1. Pre-aggregate user scores into an array
  SELECT 
    owner_user_id, 
    ARRAY_AGG(score) AS scores
  FROM 
    `bigquery-public-data.stackoverflow.posts_questions`
  WHERE 
    owner_user_id IS NOT NULL
  GROUP BY 
    owner_user_id
  HAVING 
    ARRAY_LENGTH(scores) >= 5
  LIMIT 5
)
SELECT 
  owner_user_id,
  scores,
  -- 2. Cast arrays to FLOAT64 and call the public percentile Python UDF
  `bigquery-public-data.python_udfs.percentiles`(
    ARRAY(SELECT CAST(s AS FLOAT64) FROM UNNEST(scores) AS s), 
    [25.0, 50.0, 75.0, 95.0]
  ) AS score_percentiles
FROM 
  raw_user_scores;

这样,您就可以立即了解用户表现,而无需先配置权限或编写自定义 Python 代码。

验证结果

由于此查询返回嵌套数组类型(scoresscore_percentiles),因此 BigQuery Studio 中的默认表格结果 标签页可能会显示扁平化或截断的输出,从而难以检查数组元素。

如需查看结构化的嵌套输出,请执行以下操作:

  1. 在查询结果窗格中,找到标签栏(默认显示结果 标签页)。
  2. 点击 JSON 标签页。

您应该会看到一个表示行的结构化 JSON 数组,类似于以下内容:

[{
  "owner_user_id": "533463",
  "scores": ["0", "0", "-1", "0", "0", "2", "-1", "1", "0", "0", "-1", "0", "-3", "1", "1", "0", "1", "2", "3", "1", "0", "0", "1", "0", "0", "3", "6", "11", "0", "1", "0", "0", "3", "17", "0", "1", "1", "3", "5", "-2", "1", "-1", "-1", "2", "3", "0", "0", "0", "5", "0", "4", "0", "0", "0", "3", "3", "0", "140", "0", "1", "3", "0", "0", "-2", "-1", "0", "0", "2", "0", "9", "9", "0", "0", "1", "0", "0", "1", "-1", "0", "0", "0", "0"],
  "score_percentiles": ["0.0", "0.0", "1.75", "8.8500000000000085"]
}, {
  "owner_user_id": "13502536",
  "scores": ["0", "1", "0", "-5", "0", "1", "0", "1", "0", "0", "-2", "0", "1", "0", "1", "0", "0", "1", "0", "1", "0", "0"],
  "score_percentiles": ["0.0", "0.0", "1.0", "1.0"]
}, {
  "owner_user_id": "1170153",
  "scores": ["1", "0", "1", "0", "1", "0", "2", "0", "0", "0", "10", "5", "1", "0", "0", "2", "0", "2", "3", "-1", "1", "0", "1", "0", "0", "1", "0", "2", "0", "4", "0", "3", "0", "0", "2", "0", "0", "1", "0"],
  "score_percentiles": ["0.0", "0.0", "1.5", "4.1000000000000014"]
}, {
  "owner_user_id": "8558174",
  "scores": ["0", "0", "-1", "1", "2", "0"],
  "score_percentiles": ["0.0", "0.0", "0.75", "1.75"]
}, {
  "owner_user_id": "1073044",
  "scores": ["0", "1", "0", "0", "2", "2", "2", "1", "1", "1", "2", "1", "0", "2", "3", "1"],
  "score_percentiles": ["0.75", "1.0", "2.0", "2.25"]
}]

了解输出

  • scores:每个唯一用户发布的原始问题得分的完整数组。
  • score_percentiles:一个包含四个计算出的浮点值的数组。这些值与请求的百分位完全对应:[25th, 50th, 75th, and 95th] 百分位。例如,对于用户 533463,其问题的第 95 个百分位得分约为 8.85,这表明其热门问题的得分很高。

4. 通过创建自定义 UDF 以原生方式清理文本

确定目标用户后,我们希望分析其帖子内容。但是,原始论坛帖子通常包含杂乱的 HTML 标记和实体。我们需要剥离这些标记和实体,以提高可读性并降低下游模型费用。

如需了解为什么需要这样做,我们首先检查原始的未格式化的 Stack Overflow 帖子正文的实际外观。在 BigQuery Studio 控制台中运行以下查询:

SELECT
  id,
  title,
  body AS raw_html_body
FROM
  `bigquery-public-data.stackoverflow.posts_questions`
  -- Check specific questions that we will use in our final pipeline
WHERE
  id IN (9, 17, 33969)
ORDER BY
  id ASC;

如果您检查输出,您会看到格式标记(例如 <p>、<b>、<code> 等)与文本混合在一起。如果使用下游机器学习分词器直接处理这些标记,则会引入不必要的噪声,并人为地增加 token 提取费用。

为什么使用 Python UDF 来执行此操作?

在纯 SQL 中使用正则表达式 (Regex) 可靠地解析 HTML 非常脆弱,并且容易出现解析错误。直接在查询中运行强大的 Python 库(如 beautifulsoup4)提供了一种可靠的剥离标记的方法。

运行以下 DDL 查询,以在数据集中部署持久性 clean_html 函数:

CREATE OR REPLACE FUNCTION `YOUR_PROJECT_ID.python_udfs.clean_html`(html_content STRING)
RETURNS STRING
LANGUAGE python
OPTIONS (
  runtime_version = 'python-3.11',
  entry_point = 'strip_tags',
  packages = ['beautifulsoup4>=4.12.0']
) AS r'''
from bs4 import BeautifulSoup

def strip_tags(html_content):
    if not html_content:
        return ""
    soup = BeautifulSoup(html_content, "html.parser")
    return soup.get_text(separator=" ")
''';

使用简单查询验证函数的输出:

SELECT `YOUR_PROJECT_ID.python_udfs.clean_html`('<p>Hello <b>world</b>!</p>') AS cleaned_text;

您应该会看到剥离的文本,其中不包含 HTML 元素:

+----------------+
| cleaned_text   |
+----------------+
| Hello  world ! |
+----------------+

5. 安全外部集成和高级机器学习处理

现在我们有了干净的文本,需要为机器学习模型或大语言模型 (LLM)(如 Gemma)做好准备。LLM 无法直接读取原始文本;它们处理的是数值 token ID。

如需将干净的文本转换为 token,我们将导入 Hugging Face 的 transformers 库,并直接在数据库中加载预训练的 Google T5 分词器。

创建 Cloud 资源连接

BigQuery Studio 控制台 中运行以下查询,以建立安全连接:

CREATE CONNECTION IF NOT EXISTS `YOUR_PROJECT_ID.us.external_api_connection`
OPTIONS (
  connection_type = "CLOUD_RESOURCE",
  friendly_name = "Hugging Face Hub Egress Connection",
  description = "Connection used to securely download model configs from public ML hubs"
);

创建分词器 UDF

现在,部署自定义分词器 UDF。请注意 get_tokenizer() 辅助函数如何在尝试下载之前检查全局变量 tokenizer 是否已初始化:

CREATE OR REPLACE FUNCTION `YOUR_PROJECT_ID.python_udfs.tokenize`(text STRING)
RETURNS ARRAY<INT64>
LANGUAGE python
WITH CONNECTION `YOUR_PROJECT_ID.us.external_api_connection`
OPTIONS (
  runtime_version = 'python-3.11',
  entry_point = 'tokenize',
  packages = ['transformers', 'sentencepiece']
) AS r'''
from transformers import T5TokenizerFast

# Initialize global variable for in-memory container caching
tokenizer = None

def get_tokenizer():
    global tokenizer
    if tokenizer is None:
        # Securely download T5 tokenizer config from Hugging Face Hub (runs once per warm container)
        tokenizer = T5TokenizerFast.from_pretrained("t5-base")
    return tokenizer

def tokenize(text):
    if not text:
        return []
    try:
        t = get_tokenizer()
        # Convert raw clean text into integer token IDs
        return [int(x) for x in t.encode(text)]
    except Exception:
        return []
''';

使用简单查询测试分词器,以验证它是否成功下载了资产并返回了整数 ID 数组:

SELECT `YOUR_PROJECT_ID.python_udfs.tokenize`('Hello world!') AS token_ids;

切换到查询结果面板中的 JSON 标签页,查看结构化数组:

[
  {
    "token_ids": ["8774", "296", "55", "1"]
  }
]

6. 运行端到端预处理流水线

现在,流水线的所有三个步骤都已准备就绪,我们可以使用通用表表达式 (CTE) 将它们链接到单个 SQL 查询中。

此流水线代表了现代数据工程工作流:

  1. 使用公共百分位 UDF 隔离活跃用户及其得分最高的问题。
  2. 使用我们的 clean_html UDF 在本地剥离文本中的原始 HTML 格式。
  3. 使用我们的缓存 tokenize UDF 将清理后的文本转换为 token 数组。

BigQuery Studio 控制台 中执行以下流水线查询:

WITH raw_user_scores AS (
  -- Step 1: Pre-aggregate scores to safely run percentiles with deterministic ordering
  SELECT 
    owner_user_id, 
    ARRAY_AGG(score ORDER BY id ASC) AS scores
  FROM 
    `bigquery-public-data.stackoverflow.posts_questions`
  WHERE 
    owner_user_id IS NOT NULL
  GROUP BY 
    owner_user_id
  HAVING 
    ARRAY_LENGTH(scores) >= 5
  ORDER BY 
    owner_user_id ASC
  LIMIT 3
),
active_users AS (
  -- Step 1: Extract exact percentile limits using the public UDF)
  SELECT 
    owner_user_id,
    percentiles_arr AS score_percentiles,
    -- Extract the 95th percentile score from the array's 4th element (OFFSET 3) directly
    percentiles_arr[OFFSET(3)] AS p95_score
  FROM (
    SELECT 
      owner_user_id,
      `bigquery-public-data.python_udfs.percentiles`(
        ARRAY(SELECT CAST(s AS FLOAT64) FROM UNNEST(scores) AS s), 
        [25.0, 50.0, 75.0, 95.0]
      ) AS percentiles_arr
    FROM 
      raw_user_scores
  )
),
target_questions AS (
  -- Isolate high-scoring questions from active users
  SELECT 
    q.id,
    q.owner_user_id,
    q.title,
    q.body AS raw_body,
    u.score_percentiles
  FROM 
    `bigquery-public-data.stackoverflow.posts_questions` q
  JOIN 
    active_users u ON q.owner_user_id = u.owner_user_id
  WHERE 
    -- Explicit cast for robust comparison
    q.score >= CAST(u.p95_score AS FLOAT64)
),
cleaned_data AS (
  -- Step 2: Clean HTML tags natively
  SELECT 
    id,
    owner_user_id,
    title,
    score_percentiles,
    `YOUR_PROJECT_ID.python_udfs.clean_html`(raw_body) AS cleaned_body
  FROM 
    target_questions
),
tokenized_data AS (
  -- Step 3: Perform local ML tokenization on the clean preview text
  SELECT 
    id,
    owner_user_id,
    title,
    score_percentiles,
    SUBSTR(cleaned_body, 1, 120) AS cleaned_body_preview,
    `YOUR_PROJECT_ID.python_udfs.tokenize`(SUBSTR(cleaned_body, 1, 120)) AS token_ids
  FROM 
    cleaned_data
)
SELECT 
  id,
  owner_user_id,
  title,
  score_percentiles,
  cleaned_body_preview AS cleaned_body,
  token_ids,
  ARRAY_LENGTH(token_ids) AS token_count
FROM 
  tokenized_data
ORDER BY 
  id ASC;

切换到 BigQuery Studio 中的 JSON 标签页,检查结构化输出。

[{
  "id": "9",
  "owner_user_id": "1",
  "title": "How do I calculate someone\u0027s age based on a DateTime type birthday?",
  "score_percentiles": ["22.5", "61.5", "346.75", "1762.0"],
  "cleaned_body": "Given a DateTime representing a person\u0027s birthday, how do I calculate their age in years?",
  "token_ids": ["9246", "3", "9", "7678", "13368", "9085", "3", "9", "568", "31", "7", "3591", "6", "149", "103", "27", "11837", "70", "1246", "16", "203", "58", "1"],
  "token_count": "23"
}, {
  "id": "17",
  "owner_user_id": "2",
  "title": "Binary Data in MySQL",
  "score_percentiles": ["3.5", "10.0", "90.0", "184.09999999999997"],
  "cleaned_body": "How do I store binary data in MySQL ?",
  "token_ids": ["571", "103", "27", "1078", "14865", "331", "16", "27563", "3", "58", "1"],
  "token_count": "11"
}, {
  "id": "33969",
  "owner_user_id": "3",
  "title": "Best way to implement request throttling in ASP.NET MVC?",
  "score_percentiles": ["3.25", "14.0", "24.75", "175.25"],
  "cleaned_body": "We\u0027re experimenting with various ways to throttle user actions in a given time period : Limit question/answer posts Limi",
  "token_ids": ["101", "31", "60", "3", "26718", "28", "796", "1155", "12", "28731", "1139", "2874", "16", "3", "9", "787", "97", "1059", "3", "10", "18185", "822", "87", "3247", "3321", "3489", "10908", "23", "1"],
  "token_count": "29"
}]

7. 附录:流水线的工作原理和审核执行费用

本部分深入探讨了端到端预处理查询的具体机制,并演示了如何监控执行的确切槽位消耗和托管容器费用。

流水线架构分解

WITH raw_user_scores AS (
  SELECT 
    owner_user_id, 
    ARRAY_AGG(score ORDER BY id ASC) AS scores
  FROM 
    `bigquery-public-data.stackoverflow.posts_questions`
  WHERE 
    owner_user_id IS NOT NULL
  GROUP BY 
    owner_user_id
  HAVING 
    ARRAY_LENGTH(scores) >= 5
  ORDER BY 
    owner_user_id ASC
  LIMIT 3
)

第一个查询段收集活跃 Stack Overflow 贡献者的原始问题得分。它将每个用户的得分整合到单个数组 (ARRAY_AGG) 中,同时强制执行确定性排序顺序 (ORDER BY id)。数据集经过过滤,仅包含至少有五个问题的用户,以建立有效的统计基准。

active_users AS (
  SELECT 
    owner_user_id,
    percentiles_arr AS score_percentiles,
    -- Extract the 95th percentile score from the array's 4th element (OFFSET 3) directly
    percentiles_arr[OFFSET(3)] AS p95_score
  FROM (
    SELECT 
      owner_user_id,
      `bigquery-public-data.python_udfs.percentiles`(
        ARRAY(SELECT CAST(s AS FLOAT64) FROM UNNEST(scores) AS s), 
        [25.0, 50.0, 75.0, 95.0]
      ) AS percentiles_arr
    FROM 
      raw_user_scores
  )
)

如需确定热门贡献者,此段使用公共 percentiles Python UDF 查找精确的得分分布(第 25 个、第 50 个、第 75 个和第 95 个百分位)。为避免多次执行此计算密集型 UDF,计算封装在嵌套的子查询中。然后,直接从索引位置三 (OFFSET(3)) 的结果数组中检索第 95 个百分位基准。

target_questions AS (
  -- Isolate high-scoring questions from active users
  SELECT 
    q.id,
    q.owner_user_id,
    q.title,
    q.body AS raw_body,
    u.score_percentiles
  FROM 
    `bigquery-public-data.stackoverflow.posts_questions` q
  JOIN 
    active_users u ON q.owner_user_id = u.owner_user_id
  WHERE 
    -- Explicit cast for robust comparison
    q.score >= CAST(u.p95_score AS FLOAT64)
)

原始问题与活跃用户列表联接,以检索达到或超过第 95 个百分位阈值的帖子。为防止数据库类型比较错误,基准得分在评估之前通过 CAST 运算显式转换为 FLOAT64 类型。

cleaned_data AS (
  -- Clean HTML tags natively
  SELECT 
    id,
    owner_user_id,
    title,
    score_percentiles,
    `YOUR_PROJECT_ID.python_udfs.clean_html`(raw_body) AS cleaned_body
  FROM 
    target_questions
)

原始帖子正文经常包含杂乱的标记和 HTML 样板,这些标记和样板会降低下游机器学习输入。流水线不会使用复杂的正则表达式,而是调用我们的自定义 clean_html Python UDF。它在隔离的容器内动态启动 Python 运行时,使用 BeautifulSoup 库干净地剥离元素并输出纯文本。

tokenized_data AS (
  -- Perform local ML tokenization on the clean preview text (called only once)
  SELECT 
    id,
    owner_user_id,
    title,
    score_percentiles,
    SUBSTR(cleaned_body, 1, 120) AS cleaned_body_preview,
    `YOUR_PROJECT_ID.python_udfs.tokenize`(SUBSTR(cleaned_body, 1, 120)) AS token_ids
  FROM 
    cleaned_data
)

如需为生成式模型提取准备干净的文本预览,流水线会对 120 个字符的切片调用我们的自定义 tokenize Python UDF。UDF 安全地访问 Hugging Face Hub 以下载 Google T5 分词器参数。由于分词器实例加载到全局变量中,因此暖容器会缓存配置,让后续行在内存中快速进行分词,而不会产生网络延迟。

SELECT 
  id,
  owner_user_id,
  title,
  score_percentiles,
  cleaned_body_preview AS cleaned_body,
  token_ids,
  ARRAY_LENGTH(token_ids) AS token_count
FROM 
  tokenized_data
ORDER BY 
  id ASC;

最后一个查询块输出已处理的数据集。BigQuery 的原生 ARRAY_LENGTH 函数直接应用于预先计算的 token_ids 数组,而不是再次执行分词 UDF 来计算生成的 token。此策略减少了冗余的 CPU 周期、容器运算和总体执行费用。

审核槽位消耗和托管 UDF 费用

虽然 BigQuery 直接在 Google Cloud 控制台界面中推出了全面的费用可见性信息中心,但工程师可以使用 BigQuery 作业 ID 以编程方式审核任何查询的确切槽位消耗和托管容器执行费用。

如需审核查询执行情况,请找到您的任务 ID。

  1. 在 BigQuery Studio 中,您可以通过前往控制台底部的查询历史记录 标签页找到此 ID
  2. 点击已执行的流水线查询
  3. 作业信息 详细信息面板中,找到任务 ID 字段。

确定纯任务 ID 后,请在以下查询中替换 JOB_ID,并在 BigQuery Studio 中执行该查询:

SELECT 
  job_id,
  total_slot_ms,
  external_service_costs
FROM 
  `YOUR_PROJECT_ID.region-us`.INFORMATION_SCHEMA.JOBS
WHERE 
  job_id = "JOB_ID";

切换到 BigQuery Studio 中的 JSON 标签页,检查结构化输出。您应该会收到类似于以下内容的载荷:

[{
  "job_id": "bquxjob_1234f5a_67ea8c9051a",
  "total_slot_ms": "815459",
  "external_service_costs": [{
    "external_service": "MANAGED_ROUTINE_EXECUTION",
    "bytes_processed": null,
    "bytes_billed": null,
    "slot_ms": "3000",
    "reserved_slot_count": null,
    "billing_method": "SERVICES_SKU"
  }]
}]

了解输出:

  • total_slot_ms:所有查询阶段使用的总计算时间(以毫秒为单位)。对于此统一流水线,执行时间通常平均约为 815k 槽位毫秒
  • external_service_costs:一个数组,用于细分标准 BigQuery 分析引擎之外使用的资源。
  • external_service:值“MANAGED_ROUTINE_EXECUTION”确认费用专门属于托管自定义 Python UDF 环境的无服务器容器执行。
  • slot_ms:值“3000”表示在暖容器运行时内执行 Python 逻辑所消耗的专用计算资源的确切毫秒数。
  • billing_method:值“SERVICES_SKU”表示这些本地化容器费用通过专用 BigQuery Services SKU 根据容器执行时长和内存开销动态计费。按照美国标准多区域计算价格(每槽位小时 0.06 美元,请参阅 BigQuery Services 价格页面),3,000 槽位毫秒的纯执行费用计算为 (3,000 毫秒 / 3,600,000 毫秒) * 0.06 美元 = 0.00005 美元,这表明工作流具有成本效益。

8. 清理云资源

为避免产生持续费用或消耗项目配额,请在 Cloud Shell 中删除 BigQuery 数据集和连接:

# Cleanup BigQuery routines
bq rm -f --routine ${PROJECT_ID}:${BQ_DATASET}.clean_html
bq rm -f --routine ${PROJECT_ID}:${BQ_DATASET}.tokenize

# Cleanup connection
bq rm -f --connection --location=${REGION} ${PROJECT_ID}.${REGION}.${BQ_RESOURCE_CONN}

# Cleanup BigQuery Dataset
bq rm -r -f -d ${PROJECT_ID}:${BQ_DATASET}

9. 恭喜!

您已完成有关在 BigQuery 无服务器运行时中构建和保护 Python UDF 的 Codelab。

在此 Codelab 中,您学习了如何:

  • 使用公共 UDF 探索数据: 在 Stack Overflow 数据集上调用预编译的公共 Python UDF,以对聚合数组执行数学百分位运算。
  • 集成第三方软件包: 部署利用标准 Python 运行时和 beautifulsoup4 库的自定义持久性 UDF,以在 SQL 查询中以原生方式剥离原始 HTML 标记。
  • 配置安全外部连接: 创建 BigQuery Cloud 资源连接,以安全地授予隔离的 UDF 容器出站网络访问权限,从而提取外部资产,而无需对凭据进行硬编码。
  • 使用内存中缓存实现本地分词: 导入 Hugging Face transformers 库以加载 T5 分词器,利用全局变量缓存配置文件并在暖容器内处理行。
  • 审核执行性能和费用: 使用 BigQuery 作业 ID 以编程方式查询区域 INFORMATION_SCHEMA.JOBS 视图,以跟踪槽位消耗 (total_slot_ms) 和容器使用费用 (external_service_costs)。

后续操作