El poder de Python en BigQuery con UDF administradas

1. Introducción

El lenguaje de consulta estructurado (SQL) es el estándar de la industria para el análisis de almacenes de datos. Sin embargo, expresar lógica procedimental compleja, cálculos matemáticos, limpieza de texto o flujos de trabajo de preparación de aprendizaje automático en SQL puro puede ser muy difícil.

Históricamente, los equipos de datos extrajeron conjuntos de datos masivos de BigQuery cuando se requería un procesamiento personalizado complejo en Python, los procesaron en máquinas virtuales o clústeres personalizados externos y volvieron a cargar los resultados. Este enfoque introduce una alta latencia de red, aumenta los riesgos de cumplimiento al mover los datos y genera una sobrecarga en la administración de la infraestructura.

Las funciones definidas por el usuario (UDF) de Python administradas por BigQuery resuelven estos problemas ejecutando código personalizado en recursos sin servidores que se ajustan automáticamente a millones de filas. Google Cloud administra la compilación, la creación de imágenes, la aplicación de parches de seguridad y la ejecución, lo que te permite realizar cálculos personalizados directamente donde residen tus datos.

En este codelab, compilarás una canalización de análisis y preprocesamiento de texto en los datos de la comunidad de Stack Overflow, y la prepararás para la generación de informes y el aprendizaje automático posteriores.

Requisitos previos

  • Un proyecto de Google Cloud con facturación habilitada.
  • Conocimientos básicos de los conceptos de SQL, IAM y BigQuery

Qué aprenderás

  • Cómo llamar a una UDF pública de Python precompilada en un conjunto de datos públicos para analizar las distribuciones de datos
  • Cómo implementar tu propia UDF personalizada de Python con beautifulsoup4 para limpiar datos no estructurados
  • Cómo configurar una conexión de recurso de la nube de BigQuery para descargar de forma segura recursos de aprendizaje automático y realizar la tokenización local de AA con la biblioteca de Transformers de Hugging Face usando el almacenamiento en caché de contenedores en la memoria
  • Cómo encadenar estos pasos en una sola canalización de SQL de alto rendimiento

2. Configuración y requisitos

Inicia Cloud Shell

Si bien Google Cloud y Spanner se pueden operar de manera remota desde tu laptop, en este codelab usarás Google Cloud Shell, un entorno de línea de comandos que se ejecuta en la nube.

  1. Navega a la consola de Google Cloud y, luego, selecciona o crea un proyecto de Google Cloud.
  2. ⚠️ Anota el ID del proyecto. La usarás a lo largo de este lab.

39b6a5563d69ccfb.png

  1. Abre Cloud Shell en una pestaña nueva: https://shell.cloud.google.com/.
  2. Si se te solicita, haz clic en Autorizar.
  3. Reemplaza PROJECT_ID y pega el siguiente comando en la terminal:
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

Aplica las variables a tu sesión activa:

source ./env.sh

Habilita las APIs y crea un conjunto de datos de BigQuery

Habilita los servicios de Google Cloud necesarios en tu proyecto y crea el conjunto de datos de destino:

# 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. Explora las distribuciones de datos con una UDF pública de Python

Antes de implementar código personalizado, es útil explorar el conjunto de datos y filtrar el ruido de baja calidad. En este paso, analizarás las preguntas de StackOverflow para encontrar usuarios activos y comprender la distribución estadística de sus puntuaciones de preguntas.

¿Por qué usar una UDF de Python para esto?

Calcular varios percentiles exactos (como el percentil 25, el 50, el 75 y el 95) en arrays de datos agrupados es complejo y requiere muchos recursos en SQL puro. Las funciones analíticas de SQL estándar, como PERCENTILE_CONT, esperan columnas planas de filas en lugar de arrays anidados. Para calcular los percentiles exactos de los arrays previamente agregados por fila, deberías escribir subconsultas detalladas que aniden, ordenen y vuelvan a agregar para cada métrica de percentil, lo que es ineficiente.

Si usas NumPy, la biblioteca científica altamente optimizada de Python dentro de una UDF, puedes calcular percentiles matemáticos exactos en un array de números con una sola línea de código.

Ejecución

Google Cloud aloja varias UDF públicas precompiladas (haz clic en la pestaña Rutinas). Dado que BigQuery requiere una coincidencia de tipos explícita, usaremos una expresión de tabla común (CTE) para agregar previamente los datos y convertir los arrays de números enteros en arrays de números de punto flotante con una expresión UNNEST.

Ejecuta la siguiente consulta en la consola de 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;

Esto te permite comprender el rendimiento de los usuarios de inmediato sin necesidad de configurar permisos ni escribir código personalizado de Python primero.

Cómo verificar los resultados

Dado que esta consulta devuelve tipos de array anidados (scores y score_percentiles), es posible que la pestaña tabular predeterminada Resultados en BigQuery Studio muestre un resultado aplanado o truncado, lo que dificulta la inspección de los elementos del array.

Para ver el resultado estructurado y anidado, haz lo siguiente:

  1. En el panel de resultados de la consulta, busca la barra de pestañas (que, de forma predeterminada, se establece en Resultados).
  2. Haz clic en la pestaña JSON.

Deberías ver un array JSON estructurado que representa las filas, similar al siguiente:

[{
  "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"]
}]

Cómo comprender el resultado

  • scores: Es el array completo de las puntuaciones sin procesar de las preguntas que publicó cada usuario único.
  • score_percentiles: Es un array que contiene cuatro valores de punto flotante calculados. Estos corresponden exactamente a los percentiles solicitados: percentiles [25th, 50th, 75th, and 95th]. Por ejemplo, para el usuario 533463, la puntuación del percentil 95 de sus preguntas es de aproximadamente 8.85, lo que indica que sus preguntas principales obtienen una puntuación alta.

4. Cómo limpiar texto de forma nativa creando una UDF personalizada

Una vez que se identifican los usuarios objetivo, queremos analizar el contenido de sus publicaciones. Sin embargo, las publicaciones sin procesar de los foros suelen contener etiquetas y entidades HTML desordenadas. Debemos quitar estos elementos para mejorar la legibilidad y reducir los costos del modelo de nivel inferior.

Para comprender por qué es necesario, primero inspeccionemos cómo se ve realmente el cuerpo sin formato de la publicación de Stack Overflow. Ejecuta la siguiente consulta en la consola de 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;

Si examinas el resultado, verás una combinación de etiquetas de formato, como <p>, <b>, <code> y otras integradas en el texto. Procesarlos directamente con los tokenizadores de aprendizaje automático posteriores introduciría ruido innecesario y aumentaría artificialmente los costos de incorporación de tokens.

¿Por qué usar una UDF de Python para esto?

Analizar HTML de forma confiable con expresiones regulares (regex) en SQL puro es frágil y propenso a errores de análisis. Ejecutar una biblioteca de Python sólida, como beautifulsoup4, directamente en tus consultas ofrece una forma confiable de quitar etiquetas.

Ejecuta la siguiente consulta DDL para implementar la función persistente clean_html en tu conjunto de datos:

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=" ")
''';

Verifica el resultado de la función con una consulta simple:

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

Deberías ver el texto sin elementos HTML:

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

5. Integraciones externas seguras y procesamiento avanzado de AA

Ahora que tenemos texto limpio, debemos prepararlo para los modelos de aprendizaje automático o los modelos de lenguaje grandes (LLM) como Gemma. Los LLM no pueden leer texto sin procesar directamente, sino que procesan IDs de tokens numéricos.

Para convertir nuestro texto limpio en tokens, importaremos la biblioteca transformers de Hugging Face y cargaremos un tokenizador de Google T5 previamente entrenado directamente en nuestra base de datos.

Crea la conexión de recursos de Cloud

Ejecuta la siguiente consulta en la consola de BigQuery Studio para establecer una conexión segura:

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"
);

Crea la UDF del tokenizador

Ahora, implementa la UDF del tokenizador personalizado. Observa cómo la función auxiliar get_tokenizer() verifica si la variable global tokenizer ya se inicializó antes de intentar una descarga:

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 []
''';

Prueba el tokenizador con una consulta simple para verificar que descargue correctamente el recurso y muestre un array de IDs de números enteros:

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

Cambia a la pestaña JSON en el panel de resultados de la consulta para ver el array estructurado:

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

6. Ejecuta la canalización de procesamiento previo de extremo a extremo

Ahora que los tres pasos de nuestra canalización están listos, podemos encadenarlos en una sola consulta en SQL con expresiones de tabla comunes (CTE).

Esta canalización representa un flujo de trabajo moderno de ingeniería de datos:

  1. Aísla a los usuarios activos y sus preguntas con la puntuación más alta con la UDF de percentil pública.
  2. Quita el formato HTML sin procesar del texto de forma local con nuestra UDF clean_html.
  3. Convierte el texto limpio en arrays de tokens con nuestra UDF de tokenización almacenada en caché.

Ejecuta la siguiente consulta de canalización en la consola de 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;

Cambia a la pestaña JSON en BigQuery Studio para examinar el resultado estructurado.

[{
  "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. Apéndice: Cómo funciona la canalización y cómo auditar los costos de ejecución

En esta sección, se profundiza en los mecanismos específicos de la consulta de preprocesamiento de extremo a extremo y se muestra cómo supervisar el consumo exacto de ranuras y los costos de los contenedores administrados de tu ejecución.

Desglose arquitectónico de la canalización

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
)

Este primer segmento de la consulta recopila las puntuaciones sin procesar de las preguntas para los colaboradores activos de Stack Overflow. Consolida las puntuaciones de cada usuario en un solo array (ARRAY_AGG) y aplica un orden de clasificación determinístico (ORDER BY id). El conjunto de datos se filtra para incluir solo a los usuarios con al menos cinco preguntas para establecer un modelo de referencia estadístico válido.

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
  )
)

Para identificar a los principales colaboradores, este segmento usa la UDF pública de Python percentiles para encontrar distribuciones de puntuación exactas (percentiles 25, 50, 75 y 95). Para evitar ejecutar esta UDF que requiere muchos recursos de procesamiento varias veces, el cálculo se incluye en una subconsulta anidada. Luego, la comparativa del percentil 95 se recupera directamente del array resultante en la posición del índice tres (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)
)

Las preguntas originales se combinan con la lista de usuarios activos para recuperar las publicaciones que alcanzaron o superaron el umbral del percentil 95. Para evitar errores de comparación de tipos de bases de datos, la puntuación de la comparativa se convierte de forma explícita a través de una operación CAST en un tipo FLOAT64 antes de la evaluación.

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
)

Los cuerpos de las publicaciones sin procesar suelen contener marcas desordenadas y código HTML estándar que degradan las entradas de aprendizaje automático posteriores. En lugar de usar expresiones regulares complejas, la canalización llama a nuestra UDF de Python clean_html personalizada. Activa de forma dinámica un entorno de ejecución de Python dentro de un contenedor aislado, usando la biblioteca BeautifulSoup para quitar elementos de forma limpia y generar texto simple y legible.

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
)

Para preparar la vista previa de texto limpio para la incorporación del modelo generativo, la canalización invoca nuestra UDF de Python tokenize personalizada en un segmento de 120 caracteres. La UDF se comunica de forma segura con Hugging Face Hub para descargar los parámetros del tokenizador de Google T5. Como la instancia del tokenizador se carga en una variable global, el contenedor activo almacena en caché la configuración, lo que permite que las filas posteriores se sometan a una tokenización rápida en la memoria sin latencia de red.

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;

El bloque de consulta final genera el conjunto de datos procesado. En lugar de ejecutar la UDF de tokenización por segunda vez para contar los tokens generados, la función nativa ARRAY_LENGTH de BigQuery se aplica directamente al array token_ids calculado previamente. Esta estrategia reduce los ciclos de CPU redundantes, las operaciones de contenedores y los costos generales de ejecución.

Auditoría del consumo de ranuras y los costos de las UDF administradas

Mientras BigQuery lanza paneles integrales de visibilidad de costos directamente en la IU de la consola de Google Cloud, los ingenieros pueden auditar de forma programática los costos exactos de consumo de ranuras y de ejecución de contenedores administrados de cualquier consulta con los IDs de trabajo de BigQuery.

Para auditar la ejecución de tu consulta, busca tu ID de trabajo.

  1. En BigQuery Studio, puedes encontrarlo en la pestaña Historial de consultas en la parte inferior de la consola.
  2. Haz clic en la consulta de la canalización ejecutada.
  3. En el panel de detalles Información del trabajo, busca el campo ID del trabajo.

Una vez que hayas identificado tu ID de trabajo puro, reemplaza tu JOB_ID en la siguiente consulta y ejecútala en 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";

Cambia a la pestaña JSON en BigQuery Studio para examinar el resultado estructurado. Deberías recibir una carga útil similar a la siguiente:

[{
  "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"
  }]
}]

Cómo comprender el resultado:

  • total_slot_ms: Es el tiempo total de procesamiento en milisegundos que se usó en todas las etapas de la consulta. En esta canalización unificada, la ejecución suele promediar alrededor de 815 000 milisegundos de ranura.
  • external_service_costs: Es un array que desglosa los recursos utilizados fuera del motor de análisis estándar de BigQuery.
  • external_service: El valor "MANAGED_ROUTINE_EXECUTION" confirma que el costo pertenece específicamente al hosting de ejecución de contenedores sin servidores que aloja nuestro entorno de UDF de Python personalizado.
  • slot_ms: El valor "3000" representa los milisegundos exactos de los recursos de procesamiento especializados que se consumen dentro del tiempo de ejecución del contenedor en estado activo para ejecutar la lógica de Python.
  • billing_method: El valor "SERVICES_SKU" indica que estos cargos de contenedor localizados se facturan de forma dinámica a través del SKU especializado de los servicios de BigQuery en función de la duración de la ejecución del contenedor y la sobrecarga de memoria. Con el precio estándar de procesamiento multirregional de USD 0.06 por ranura-hora (consulta la página de precios de los servicios de BigQuery), el costo de ejecución puro de 3,000 ms de ranura se calcula como (3,000 ms / 3,600,000 ms) * USD 0.06 = USD 0.00005, lo que demuestra un flujo de trabajo rentable.

8. Borra recursos de la nube

Para evitar que se generen cargos continuos o que se consuman las cuotas del proyecto, borra tu conjunto de datos y tus conexiones de BigQuery en 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. ¡Felicitaciones!

Completaste el codelab sobre la creación y protección de UDF de Python en el entorno de ejecución sin servidores de BigQuery.

En este codelab aprendiste a hacer lo siguiente:

  • Explora los datos con UDF públicas: Llama a UDF públicas de Python precompiladas en conjuntos de datos de Stack Overflow para realizar operaciones de percentiles matemáticos en arrays agregados.
  • Integrar paquetes de terceros: Implementa una UDF persistente personalizada que utilice el entorno de ejecución estándar de Python y la biblioteca beautifulsoup4 para quitar etiquetas HTML sin procesar de forma nativa dentro de tus consultas en SQL.
  • Configura conexiones externas seguras: Crea una conexión de recursos de Cloud de BigQuery para otorgar de forma segura a los contenedores de UDF aislados acceso de red saliente para recuperar recursos externos sin codificar de forma rígida las credenciales.
  • Implementa la tokenización local con almacenamiento en caché en la memoria: Importa la biblioteca transformers de Hugging Face para cargar un tokenizador T5, utilizando variables globales para almacenar en caché los archivos de configuración y procesar filas dentro de contenedores activos.
  • Audita el rendimiento y los costos de ejecución: Consulta de forma programática las vistas regionales INFORMATION_SCHEMA.JOBS con IDs de trabajos de BigQuery para hacer un seguimiento del consumo de ranuras (total_slot_ms) y los costos de uso de contenedores (external_service_costs).

¿Qué sigue?