Como criar um data lakehouse unificado para IA com o Apache Iceberg e o BigLake

1. Introdução

Neste codelab, você vai conhecer os recursos do data lakehouse unificado no Google Cloud. Você vai interagir com conjuntos de dados públicos disponibilizados pelo catálogo REST do Apache Iceberg no BigLake e, em seguida, aplicar os recursos de IA do Google Cloud a dados estruturados e não estruturados.

Você vai consultar o conjunto de dados clássico de táxis de Nova York usando o Apache Iceberg, analisar a viagem no tempo para auditar mudanças nos dados e usar o BigQuery ML e o Gemini para executar modelos de IA nos seus dados.

Atividades deste laboratório

  • Use o Google Cloud Serverless para Apache Spark e consulte os conjuntos de dados públicos do Apache Iceberg hospedados no BigLake.
  • Consultar dados estruturados no formato Apache Iceberg.
  • Demonstre a viagem no tempo no Apache Iceberg.
  • Use o BigQuery ML para treinar um modelo preditivo em dados estruturados.
  • Crie uma tabela de objetos do BigLake (dados não estruturados) e use o Gemini para analisar imagens.

O que é necessário

  • Um navegador da Web, como o Chrome.
  • Ter um projeto do Google Cloud com o faturamento ativado.

Custo e duração esperados

  • Tempo para conclusão: ~45 minutos.
  • Custo estimado: menos de US$ 2,00. Usamos conjuntos de dados públicos e consultas sem servidor para manter os custos baixos.

2. Configuração e requisitos

Nesta etapa, você vai preparar o ambiente e ativar as APIs necessárias.

Iniciar o Cloud Shell

A maioria dos comandos será executada no Google Cloud Shell.

  1. Clique em Ativar o Cloud Shell na parte de cima do console do Google Cloud.
  2. Verificar a autenticação:
    gcloud auth list
    
  3. Confirme seu projeto:
    gcloud config get project
    
  4. Se o projeto não estiver definido, use o ID do projeto:
    gcloud config set project <YOUR_PROJECT_ID>
    

Ativar APIs

Execute o comando a seguir para ativar as APIs necessárias para o BigQuery, o Cloud Resource Manager e a Vertex AI:

gcloud services enable \
  bigquery.googleapis.com \
  aiplatform.googleapis.com \
  cloudresourcemanager.googleapis.com

Configurar o ambiente e criar o bucket de dependências

  1. Defina as variáveis de ambiente no terminal:
    export PROJECT_ID=$(gcloud config get project)
    export REGION=us-central1
    export DEPS_BUCKET=$PROJECT_ID-deps-bucket
    
  2. Crie o bucket do Cloud Storage de dependências. Os scripts do PySpark são enviados aqui no momento do envio do job:
    gcloud storage buckets create gs://$DEPS_BUCKET --location=$REGION
    

3. Conectar-se ao catálogo público do Apache Iceberg

Nesta etapa, você vai se conectar a um catálogo do Apache Iceberg de produção hospedado no BigLake do Google Cloud.

Executar o Spark SQL com a CLI em lote do Serverless para Apache Spark

Vamos usar o Google Cloud Serverless para Apache Spark para executar jobs do PySpark sem precisar gerenciar a infraestrutura. Vamos configurá-lo para apontar para o catálogo público REST do BigLake.

  1. Defina as propriedades do catálogo REST do BigLake para evitar repetições.Essa configuração informa ao Spark:
    • Para usar as bibliotecas iceberg-spark-runttime e iceberg-gcp-bundle.
    • Para configurar um catálogo chamado my_catalog usando o endpoint do catálogo REST do BigLake.
    • Para usar o Google Cloud Storage (GCS) na leitura de arquivos de dados em vez do sistema de arquivos local padrão.
    • Para definir esse catálogo do my_catalog como o padrão da nossa sessão.
    • Para usar credenciais vendidas e aumentar a segurança e simplificar o acesso aos dados.
    export METASTORE_PROPERTIES="^|^spark.jars.packages=org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.10.0,org.apache.iceberg:iceberg-gcp-bundle:1.10.0|\
    spark.sql.catalog.my_catalog=org.apache.iceberg.spark.SparkCatalog|\
    spark.sql.catalog.my_catalog.type=rest|\
    spark.sql.catalog.my_catalog.uri=https://biglake.googleapis.com/iceberg/v1/restcatalog|\
    spark.sql.catalog.my_catalog.warehouse=gs://biglake-public-nyc-taxi-iceberg|\
    spark.sql.catalog.my_catalog.io-impl=org.apache.iceberg.gcp.gcs.GCSFileIO|\
    spark.sql.catalog.my_catalog.header.x-goog-user-project=$PROJECT_ID|\
    spark.sql.catalog.my_catalog.header.X-Iceberg-Access-Delegation=vended-credentials|\
    spark.sql.catalog.my_catalog.rest.auth.type=org.apache.iceberg.gcp.auth.GoogleAuthManager|\
    spark.sql.defaultCatalog=my_catalog|\
    spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions|\
    spark.log.level=ERROR"
    
  2. Crie um arquivo de consulta de teste simples:
    cat <<EOF > test.py
    from pyspark.sql import SparkSession
    
    spark = SparkSession.builder.getOrCreate()
    
    spark.sql("SHOW TABLES IN public_data").show()
    EOF
    
  3. Envie o job em lote:
    gcloud dataproc batches submit pyspark \
      --project=$PROJECT_ID \
      --region=$REGION \
      --version=2.3 \
      --properties="$METASTORE_PROPERTIES" \
      --deps-bucket=gs://$DEPS_BUCKET \
      test.py
    
    A saída será semelhante a esta:
    +-----------+----------------+-----------+
    |  namespace|       tableName|isTemporary|
    +-----------+----------------+-----------+
    |public_data|     nyc_taxicab|      false|
    |public_data|nyc_taxicab_2021|      false|
    +-----------+----------------+-----------+
    

4. Consultar dados estruturados do Iceberg

Depois de conectado, você terá acesso total em SQL aos conjuntos de dados. Vamos consultar o conjunto de dados de táxi de Nova York modelado como uma tabela do Iceberg.

Executar uma consulta de agregação padrão

Crie um arquivo chamado query.py:

cat <<EOF > query.py
from pyspark.sql import SparkSession

spark = SparkSession.builder.getOrCreate()

query = """
SELECT
  passenger_count,
  COUNT(1) AS num_trips,
  ROUND(AVG(total_amount), 2) AS avg_fare,
  ROUND(AVG(trip_distance), 2) AS avg_distance
FROM public_data.nyc_taxicab
WHERE data_file_year = 2021 AND passenger_count > 0
GROUP BY passenger_count
ORDER BY num_trips DESC
"""

spark.sql(query).show()
EOF

e envie usando o Serverless para Apache Spark:

gcloud dataproc batches submit pyspark \
  --project=$PROJECT_ID \
  --region=$REGION \
  --version=2.3 \
  --properties="$METASTORE_PROPERTIES" \
  --deps-bucket=gs://$DEPS_BUCKET \
  query.py

A saída no console será semelhante a esta:

+---------------+---------+--------+------------+
|passenger_count|num_trips|avg_fare|avg_distance|
+---------------+---------+--------+------------+
|              1| 21508009|   18.82|        3.03|
|              2|  4424746|   20.22|        3.40|
|              3|  1164846|   19.84|        3.27|
|              5|   718282|   18.88|        3.07|
|              4|   466485|   20.61|        3.44|
|              6|   452467|   18.97|        3.11|
|              7|       78|   65.24|        3.71|
|              8|       49|   57.39|        5.88|
|              9|       35|   73.26|        6.20|
|             96|        1|   17.00|        2.00|
|            112|        1|   15.00|        2.00|
+---------------+---------+--------+------------+

Por que usar o Apache Iceberg aqui?

  • Remoção de partições: a consulta filtra em data_file_year = 2021. O Iceberg permite que o mecanismo pule a verificação de dados de outros anos.
  • Agilidade do mecanismo: é possível executar isso no Spark, no Trino ou no BigQuery sem copiar dados.

5. Viagem no tempo no Apache Iceberg

Um dos recursos mais avançados do Iceberg é a viagem no tempo. Ele permite consultar dados como eles existiam em uma versão ou snapshot anterior.

Ver histórico da tabela

Crie um arquivo chamado history.py:

cat <<EOF > history.py
from pyspark.sql import SparkSession

spark = SparkSession.builder.getOrCreate()

spark.sql("SELECT * FROM public_data.nyc_taxicab.history").show()
EOF

E envie:

gcloud dataproc batches submit pyspark \
  --project=$PROJECT_ID \
  --region=$REGION \
  --version=2.3 \
  --properties="$METASTORE_PROPERTIES" \
  --deps-bucket=gs://$DEPS_BUCKET \
  history.py

Você vai ver uma saída semelhante a esta no console:

+--------------------+-------------------+-------------------+-------------------+
|     made_current_at|        snapshot_id|          parent_id|is_current_ancestor|
+--------------------+-------------------+-------------------+-------------------+
|2026-01-07 21:32:...|6333415779680505547|               NULL|               true|
|2026-01-07 21:34:...|1840345522877675925|6333415779680505547|               true|
|2026-01-07 21:36:...|7203554539964460256|1840345522877675925|               true|
|2026-01-07 21:38:...|4573466015237516024|7203554539964460256|               true|
|2026-01-07 21:40:...|3353190952148867790|4573466015237516024|               true|
|2026-01-07 21:42:...|1335547378580631681|3353190952148867790|               true|
|2026-01-07 21:44:...|8203141258229894239|1335547378580631681|               true|
|2026-01-07 21:46:...|1597048231706307813|8203141258229894239|               true|
|2026-01-07 21:48:...|6247811509231462655|1597048231706307813|               true|
|2026-01-07 21:50:...|2527184310045633322|6247811509231462655|               true|
|2026-01-07 21:52:...|2512764101237223642|2527184310045633322|               true|
|2026-01-07 21:52:...|7045957533358062548|2512764101237223642|               true|
|2026-01-07 21:53:...| 531753237516076726|7045957533358062548|               true|
|2026-01-07 21:53:...|4184653573199718274| 531753237516076726|               true|
|2026-01-07 21:54:...|5125223829492177301|4184653573199718274|               true|
|2026-01-07 21:54:...|6844673237417600305|5125223829492177301|               true|
|2026-01-07 21:54:...|6634828203344518093|6844673237417600305|               true|
|2026-01-07 21:55:...|7637728273407236194|6634828203344518093|               true|
|2026-01-07 21:55:...|3424071684958740192|7637728273407236194|               true|
|2026-01-07 21:55:...|1743746294196424254|3424071684958740192|               true|
+--------------------+-------------------+-------------------+-------------------+

Você vai ver linhas representando diferentes IDs de snapshots e quando eles foram confirmados.

Comparar a contagem de linhas atual com a anterior

Crie um arquivo chamado timetravel.py:

cat <<EOF > timetravel.py
from pyspark.sql import SparkSession

spark = SparkSession.builder.getOrCreate()

query = """
SELECT 'Current State' AS version, COUNT(*) AS count FROM public_data.nyc_taxicab
UNION ALL
SELECT 'Past State' AS version, COUNT(*) AS count FROM public_data.nyc_taxicab VERSION AS OF 4573466015237516024
"""

spark.sql(query).show()
EOF

E envie:

gcloud dataproc batches submit pyspark \
  --project=$PROJECT_ID \
  --region=$REGION \
  --version=2.3 \
  --properties="$METASTORE_PROPERTIES" \
  --deps-bucket=gs://$DEPS_BUCKET \
  timetravel.py

Você vai ver uma saída semelhante a esta no console:

+-------------+----------+
|      version|     count|
+-------------+----------+
|Current State|1293069366|
|   Past State|  72878594|
+-------------+----------+

Isso garante que você possa auditar as mudanças nos dados ao longo do tempo.

6. IA estruturada com o BigQuery ML

Agora que você já analisou os dados do Iceberg, vamos usar os recursos de IA do BigQuery. Como o catálogo público do Iceberg é somente leitura, podemos usar o BigQuery para treinar um modelo no nosso espaço de trabalho lendo tabelas públicas.

Criar um conjunto de dados local

Primeiro, crie um conjunto de dados no seu projeto para armazenar o modelo de IA usando a CLI bq:

bq mk --location=$REGION --project_id=$PROJECT_ID iceberg_ai

Treinar um modelo de regressão linear

Agora você vai treinar um modelo de regressão linear usando a tabela pública do BigLake Iceberg.

Crie um arquivo de consulta e treine o modelo usando bq query:

cat <<'EOF' > train_model.sql
CREATE OR REPLACE MODEL `iceberg_ai.predict_fare`
OPTIONS(model_type='LINEAR_REG', input_label_cols=['fare_amount']) AS
SELECT fare_amount, passenger_count, CAST(trip_distance AS FLOAT64) AS trip_distance
FROM `bigquery-public-data`.`biglake-public-nyc-taxi-iceberg`.public_data.nyc_taxicab
WHERE fare_amount > 0 AND trip_distance > 0 AND RAND() < 0.01; -- Using 1% of data to downsample
EOF

bq query --location=$REGION --use_legacy_sql=false < train_model.sql

Prever usando o modelo

Agora que o modelo está treinado, você pode usá-lo para prever valores de tarifas de novas viagens usando ML.PREDICT.

Crie um arquivo de consulta e execute a previsão usando bq query:

cat <<'EOF' > predict_fare.sql
SELECT
  predicted_fare_amount, passenger_count, trip_distance
FROM
  ML.PREDICT(MODEL `iceberg_ai.predict_fare`,
    (
    SELECT 2 AS passenger_count, 5.0 AS trip_distance
    )
  );
EOF

bq query --location=$REGION --use_legacy_sql=false < predict_fare.sql

A resposta será semelhante a esta:

+-----------------------+-----------------+---------------+
| predicted_fare_amount | passenger_count | trip_distance |
+-----------------------+-----------------+---------------+
|     14.12252095150709 |               2 |           5.0 |
+-----------------------+-----------------+---------------+

7. IA não estruturada com o BigLake

Os dados não são apenas linhas e colunas. Os data lakehouses unificados também processam dados não estruturados (imagens, PDFs). Vamos usar tabelas de objetos e referências de objetos para consultar dados não estruturados.

As tabelas de objetos são tabelas externas somente leitura no BigQuery que listam objetos em um caminho do Cloud Storage. Cada linha representa um arquivo, com colunas para metadados como uri, size e uma coluna especial ref que contém o ObjectRef.

Referências de objetos (ObjectRef) apontam para os dados reais de um único arquivo. As funções modernas do BigQuery ML (como AI.GENERATE ou AI.AGG) consomem ObjectRef para ler o conteúdo do arquivo (imagens, áudio ou texto) para análise sem carregar os bytes em uma tabela padrão.

Criar um conjunto de dados para IA não estruturada

Primeiro, crie um segundo conjunto de dados no seu projeto para armazenar as tabelas de objetos usando a CLI bq na multirregião US:

bq mk --location=US --project_id=$PROJECT_ID iceberg_object_ai

Criar uma conexão externa

Para consultar dados armazenados no Cloud Storage (tabelas de objetos e dados não estruturados) no BigQuery, crie uma conexão externa.

Execute o seguinte no Cloud Shell para criar uma conexão a recursos do Cloud:

bq mk --connection --project_id=$PROJECT_ID --location=US --connection_type=CLOUD_RESOURCE my-conn

Encontre o ID da conta de serviço criada para sua conexão:

CONNECTION_SA=$(bq show --format=json --project_id=$PROJECT_ID --connection $PROJECT_ID.us.my-conn | jq -r '.serviceAccountId // .cloudResource.serviceAccountId')

Conceda à conta de serviço os papéis de Usuário da Vertex AI e Leitor de objetos do Storage para que ela possa chamar modelos do Gemini e ler dados do GCS:

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$CONNECTION_SA" \
  --role="roles/aiplatform.user"

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$CONNECTION_SA" \
  --role="roles/storage.objectViewer"

Crie uma tabela de objetos

Vamos usar a conexão externa my-conn criada na seção anterior para acessar dados não estruturados. Crie um arquivo de consulta e a tabela de objetos usando bq query:

cat <<'EOF' > create_object_table.sql
CREATE EXTERNAL TABLE `iceberg_object_ai.sample_images`
WITH CONNECTION `us.my-conn`
OPTIONS (
  object_metadata = 'SIMPLE',
  uris = ['gs://cloud-samples-data/vision/landmark/*']
);
EOF

bq query --use_legacy_sql=false < create_object_table.sql

Usar o Gemini em dados de objetos

Agora, execute uma consulta usando o Gemini para avaliar as imagens sem fazer o download delas.

Consulte as imagens usando o SQL padrão via bq query:

cat <<EOF > query_images.sql
SELECT
  uri,
  image_analysis.description
FROM (
  SELECT
    uri,
    AI.GENERATE(
      (
        'Identify what is happening in the image.',
        ref
      ),
      connection_id => 'us.my-conn',
      endpoint => 'gemini-2.5-flash-lite',
      output_schema => 'event STRING, severity STRING, description STRING'
    ) AS image_analysis
  FROM
    iceberg_object_ai.sample_images
);
EOF

bq query --use_legacy_sql=false < query_images.sql

Exemplo de resposta:

+----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                           uri                            |                                                                                                                                                                                                                             description                                                                                                                                                                                                                             |
+----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| gs://cloud-samples-data/vision/landmark/eiffel_tower.jpg | The Eiffel Tower stands tall against a cloudy sky, overlooking the Seine River in Paris. Boats are docked along the riverbank, and trees line the opposite shore, with bridges and buildings visible in the distance.                                                                                                                                                                                                                                               |
| gs://cloud-samples-data/vision/landmark/pofa.jpg         | A wide shot shows the Palace of Fine Arts, a monumental structure in San Francisco, California. The building features a large rotunda with a dome, surrounded by colonnades. In front of the rotunda is a lagoon. Several people are walking around the grounds. The sky is blue with a few scattered clouds.                                                                                                                                                       |
| gs://cloud-samples-data/vision/landmark/st_basils.jpeg   | A monument stands in front of Saint Basil's Cathedral in Moscow under a bright blue sky with scattered white clouds. The cathedral features distinctive onion domes in various colors and patterns, including red, blue and white stripes, green and beige stripes, and red and blue diamonds. A large green tree partially obscures the left side of the cathedral. People are visible in the foreground near the base of the monument and the cathedral entrance. |
+----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Como analisar sentimentos diretamente com ObjectRefs

Embora as tabelas de objetos gerenciem referências de arquivos automaticamente, é possível interagir diretamente com esses objetos usando as referências de objetos do BigQuery para executar análises instantâneas em arquivos únicos.

Por exemplo, você pode usar um pequeno arquivo de texto armazenado no seu próprio bucket do GCS (usando a variável $DEPS_BUCKET criada anteriormente) e analisá-lo usando OBJ.MAKE_REF com bq query.

Primeiro, crie um pequeno arquivo de texto e faça upload dele no bucket:

cat <<'EOF' > review.txt
This product is fantastic! It exceeded my expectations. The quality is top-notch. I highly recommend it to everyone!
EOF

gcloud storage cp review.txt gs://${DEPS_BUCKET}/review.txt

Agora consulte o arquivo usando OBJ.MAKE_REF no SQL padrão:

cat <<EOF > sentiment_analysis.sql
SELECT
  AI.GENERATE(
    (
      'Analyze the sentiment of this text file. Is it positive, negative, or neutral? Explain why.',
      OBJ.MAKE_REF('gs://${DEPS_BUCKET}/review.txt', 'us.my-conn')
    ),
    connection_id => 'us.my-conn',
    endpoint => 'gemini-2.5-flash-lite'
  ).result AS ml_generate_text_result;
EOF

bq query --use_legacy_sql=false < sentiment_analysis.sql

Exemplo de resposta:

+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                                 ml_generate_text_result                                                                                  |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| This text file has a **strongly positive** sentiment.                                                                                                                                    |
|                                                                                                                                                                                          |
| Here's why:                                                                                                                                                                              |
|                                                                                                                                                                                          |
| *   **Positive Keywords:** The text is filled with unequivocally positive words and phrases:                                                                                             |
|     *   "fantastic"                                                                                                                                                                      |
|     *   "exceeded my expectations"                                                                                                                                                       |
|     *   "top-notch"                                                                                                                                                                      |
|     *   "highly recommend"                                                                                                                                                               |
|                                                                                                                                                                                          |
| *   **Enthusiastic Language:** The use of exclamation marks ("!") further amplifies the positive tone, indicating excitement and strong approval.                                        |
|                                                                                                                                                                                          |
| *   **Lack of Negative or Neutral Elements:** There are no words, phrases, or implications that suggest any dissatisfaction, criticism, or even indifference.                            |
|                                                                                                                                                                                          |
| In summary, the author's language is enthusiastic and uses multiple strong positive descriptors, leaving no room for doubt that their opinion of the product is overwhelmingly positive. |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

8. Limpar

Para evitar cobranças contínuas na sua conta do Google Cloud, exclua os recursos criados durante este codelab.

Excluir conjunto de dados e conexão

Execute o seguinte no Cloud Shell para excluir os conjuntos de dados e a conexão:

bq rm -r -f --location=$REGION iceberg_ai
bq rm -r -f --location=US iceberg_object_ai
bq rm --connection $PROJECT_ID.US.my-conn

Excluir buckets do GCS e arquivos locais

Limpe os buckets do GCS e os arquivos locais:

# Delete GCS buckets
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
gcloud storage rm -r gs://dataproc-temp-${REGION}-${PROJECT_NUMBER}-*
gcloud storage rm -r gs://dataproc-staging-${REGION}-${PROJECT_NUMBER}-*
gcloud storage rm -r gs://${DEPS_BUCKET}

# Delete local files
rm -f train_model.sql predict_fare.sql create_object_table.sql query_images.sql sentiment_analysis.sql test.py query.py history.py timetravel.py review.txt

Também é possível excluir todo o projeto se você o criou apenas para este laboratório.

9. Parabéns

Parabéns! Você criou um data lakehouse unificado usando o Apache Iceberg, o BigLake e a IA do BigQuery.

O que você aprendeu

  • Como se conectar e consultar catálogos REST públicos do Apache Iceberg.
  • Usar a viagem no tempo no Iceberg para auditar versões de conjuntos de dados.
  • Treinar modelos do BigQuery ML com dados estruturados.
  • Conectar dados não estruturados (imagens) usando tabelas de objetos e ObjectRef.
  • Usar o Gemini diretamente no BigQuery SQL para analisar imagens.

Documentos de referência