Расширение возможностей Python для BigQuery с помощью управляемых пользовательских функций (UDF).

1. Введение

Язык структурированных запросов (SQL) является отраслевым стандартом для анализа хранилищ данных. Однако выражение сложной процедурной логики, математических вычислений, очистки текста или подготовительных рабочих процессов для машинного обучения на чистом SQL может быть весьма сложной задачей.

Исторически сложилось так, что команды, работающие с данными, извлекали огромные массивы данных из BigQuery , когда требовалась сложная обработка на Python, обрабатывали их на внешних виртуальных машинах или кластерах, а затем загружали результаты обратно. Такой подход приводит к высокой задержке в сети, увеличивает риски соблюдения нормативных требований из-за перемещения данных и создает дополнительные затраты на управление инфраструктурой.

Управляемые пользовательские функции Python (UDF) в BigQuery решают эти проблемы, выполняя пользовательский код на бессерверных ресурсах, которые автоматически масштабируются до миллионов строк. Google Cloud управляет компиляцией, созданием образов, обновлением безопасности и выполнением, позволяя вам запускать пользовательские вычисления непосредственно там, где находятся ваши данные.

В этом практическом занятии вы создадите аналитический конвейер и систему предварительной обработки текста на основе данных сообщества StackOverflow, подготовив их для последующей отчетности и машинного обучения.

Предварительные требования

  • Проект Google Cloud с включенной функцией выставления счетов.
  • Базовое понимание концепций SQL, IAM и BigQuery.

Что вы узнаете

  • Как вызвать предварительно скомпилированную общедоступную пользовательскую функцию Python для анализа распределения данных в общедоступном наборе данных.
  • Как развернуть собственную пользовательскую функцию Python с помощью beautifulsoup4 для очистки неструктурированных данных.
  • Как настроить подключение к облачным ресурсам BigQuery для безопасной загрузки ресурсов машинного обучения и выполнения локальной токенизации машинного обучения с помощью библиотеки Hugging Face transformers, используя кэширование контейнеров в оперативной памяти.
  • Как объединить эти шаги в единый высокопроизводительный SQL-конвейер.

2. Настройка и требования

Запустить Cloud Shell

Хотя Google Cloud можно управлять удаленно с ноутбука, в этом практическом занятии вы будете использовать Google Cloud Shell — среду командной строки, работающую в облаке.

  1. Перейдите в консоль Google Cloud , затем выберите или создайте проект Google Cloud .
  2. ⚠️ Обратите внимание на идентификатор проекта. Вы будете использовать его на протяжении всей лабораторной работы.

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.

Перед развертыванием пользовательского кода полезно изучить набор данных и отфильтровать некачественный шум. На этом этапе вы проанализируете вопросы на StackOverflow, чтобы найти активных пользователей и понять статистическое распределение оценок их вопросов.

Зачем для этого использовать пользовательскую функцию Python?

Вычисление нескольких точных процентилей (таких как 25-й, 50-й, 75-й и 95-й процентили) для сгруппированных массивов данных является сложной и ресурсоемкой задачей в чистом SQL. Стандартные аналитические функции SQL, такие как PERCENTILE_CONT ожидают плоские столбцы строк, а не вложенные массивы. Для вычисления точных процентилей предварительно агрегированных массивов для каждой строки пришлось бы писать многословные подзапросы, которые разворачивают, сортируют и повторно агрегируют для каждого показателя процентиля, что неэффективно.

Используя NumPy, высокооптимизированную научную библиотеку Python, внутри пользовательской функции (UDF), вы можете вычислить точные математические процентили для массива чисел всего одной строкой кода.

Исполнение

В 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.

Проверка результатов

Поскольку этот запрос возвращает вложенные массивы ( scores и score_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> и других, встроенных в текст. Обработка их напрямую с помощью последующих токенизаторов машинного обучения приведет к появлению ненужного шума и искусственному завышению затрат на обработку токенов.

Зачем для этого использовать пользовательскую функцию Python?

Надежный анализ HTML с использованием регулярных выражений (Regex) в чистом SQL является ненадежным и подверженным ошибкам анализа. Использование надежной библиотеки 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 не могут напрямую считывать необработанный текст; они обрабатывают числовые идентификаторы токенов.

Для преобразования нашего чистого текста в токены мы импортируем библиотеку transformers от Hugging Face и загрузим предварительно обученный токенизатор Google T5 непосредственно в нашу базу данных.

Создайте подключение к облачному ресурсу.

Выполните следующий запрос в консоли 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"
);

Создайте пользовательскую функцию токенизатора.

Теперь разверните пользовательскую функцию токенизации. Обратите внимание, как вспомогательная функция 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 []
''';

Протестируйте токенизатор с помощью простого запроса, чтобы убедиться, что он успешно загружает ресурс и возвращает массив целочисленных идентификаторов:

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

Перейдите на вкладку JSON в панели результатов запроса, чтобы увидеть структурированный массив:

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

6. Запустите сквозной конвейер предварительной обработки.

Теперь, когда все три этапа нашего конвейера готовы, мы можем объединить их в один SQL-запрос, используя общие табличные выражения (CTE).

Данный конвейер представляет собой современный рабочий процесс обработки данных:

  1. Выделите активных пользователей и вопросы с наивысшим рейтингом, используя общедоступную функцию UDF, отображающую процентили .
  2. Удалите локально необработанное HTML-форматирование из текста, используя нашу пользовательскую функцию clean_html .
  3. Преобразуйте очищенный текст в массивы токенов, используя нашу кэшированную пользовательскую функцию токенизации .

Выполните следующий запрос конвейера в консоли 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;

Перейдите на вкладку JSON в BigQuery Studio, чтобы изучить структурированный вывод.

[{
  "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
  )
)

Для определения лучших участников в этом сегменте используется общедоступная пользовательская функция Python для расчета percentiles , позволяющая найти точные распределения оценок (25-й, 50-й, 75-й и 95-й процентили). Чтобы избежать многократного выполнения этой ресурсоемкой пользовательской функции, вычисление заключено во вложенный подзапрос. Затем эталон 95-го процентиля извлекается непосредственно из результирующего массива по индексу три ( OFFSET(3) ).

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
)

В исходных телах POST-запросов часто содержится неаккуратная разметка и шаблонный HTML-код, что ухудшает качество входных данных для машинного обучения. Вместо использования сложных регулярных выражений конвейер вызывает нашу пользовательскую функцию Python clean_html . Она динамически запускает среду выполнения 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
)

Для подготовки предварительного просмотра чистого текста к обработке генеративной моделью конвейер вызывает нашу пользовательскую функцию tokenize на Python для фрагмента текста длиной 120 символов. Функция безопасно обращается к 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;

В заключительном блоке запроса выводится обработанный набор данных. Вместо повторного выполнения функции токенизации для подсчета сгенерированных токенов, к предварительно вычисленному массиву token_ids применяется встроенная функция BigQuery ARRAY_LENGTH . Такая стратегия позволяет сократить избыточные циклы ЦП, операции с контейнерами и общие затраты на выполнение.

Аудит потребления слотов и управляемых затрат UDF.

В то время как BigQuery внедряет комплексные панели мониторинга затрат непосредственно в пользовательский интерфейс консоли Google Cloud, инженеры могут программно проверять точные затраты на использование слотов и выполнение управляемых контейнеров для любого запроса, используя идентификаторы заданий BigQuery.

Для проверки выполнения запроса найдите свой идентификатор задания (Job ID).

  1. В BigQuery Studio это можно найти, перейдя на вкладку «История запросов» в нижней части консоли.
  2. Щелкните по выполненному запросу конвейера.
  3. В панели сведений о вакансии найдите поле «Идентификатор вакансии» .

После того, как вы определили свой чистый идентификатор задания (Job 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";

Перейдите на вкладку JSON в BigQuery Studio, чтобы изучить структурированный вывод. Вы должны получить данные, похожие на следующие:

[{
  "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 : Общее время вычислений в миллисекундах, затраченное на все этапы выполнения запроса. Для этого унифицированного конвейера выполнение обычно занимает в среднем около 815 тыс. слотовых миллисекунд.
  • external_service_costs : Массив, содержащий разбивку ресурсов, используемых вне стандартного механизма анализа BigQuery.
  • external_service : Значение " MANAGED_ROUTINE_EXECUTION " подтверждает, что затраты относятся непосредственно к выполнению бессерверного контейнера, в котором размещена наша пользовательская среда UDF на Python.
  • slot_ms : Значение " 3000 " точно указывает количество миллисекунд, затраченных на использование специализированных вычислительных ресурсов внутри «горячего» контейнера для выполнения логики Python.
  • billing_method : Значение " SERVICES_SKU " указывает на то, что эти локализованные сборы за контейнеры выставляются динамически через специализированный SKU сервисов BigQuery на основе продолжительности выполнения контейнера и накладных расходов на память. При стандартной цене вычислительных ресурсов в нескольких регионах США в размере 0,06 доллара США за слот-час (см. страницу цен на сервисы BigQuery ), чистая стоимость выполнения 3000 слот-мс рассчитывается как (3000 мс / 3 600 000 мс) * 0,06 доллара США = 0,00005 доллара США , что демонстрирует экономичность рабочего процесса.

8. Очистка облачных ресурсов

Чтобы избежать постоянных расходов или исчерпания проектных квот, удалите свой набор данных BigQuery и подключения в Cloud Shell:

# 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. Поздравляем!

Вы завершили практическое занятие по созданию и обеспечению безопасности пользовательских функций Python в среде выполнения BigQuery Serverless.

В этом практическом занятии вы научились:

  • Исследуйте данные с помощью общедоступных пользовательских функций (UDF): вызывайте предварительно скомпилированные общедоступные пользовательские функции Python на наборах данных Stack Overflow для выполнения математических операций с процентилями над агрегированными массивами.
  • Интеграция сторонних пакетов: разверните пользовательскую функцию сохранения данных, используя стандартную среду выполнения Python и библиотеку beautifulsoup4 , для удаления необработанных HTML-тегов непосредственно в ваших SQL-запросах.
  • Настройка безопасных внешних подключений: создайте подключение к облачным ресурсам BigQuery, чтобы безопасно предоставлять изолированным контейнерам UDF исходящий сетевой доступ для получения внешних ресурсов без жесткого кодирования учетных данных.
  • Реализация локальной токенизации с кэшированием в памяти: импортируйте библиотеку transformers Hugging Face для загрузки токенизатора T5, используя глобальные переменные для кэширования файлов конфигурации и обработки строк внутри «горячих» контейнеров.
  • Аудит производительности и затрат при выполнении: Программно запрашивайте региональные представления INFORMATION_SCHEMA.JOBS, используя идентификаторы заданий BigQuery, для отслеживания потребления слотов ( total_slot_ms ) и затрат на использование контейнеров ( external_service_costs ).

Что дальше?