DE Trilhas

M06 — Unity Catalog + Governança

Duração: 3h Pré-requisitos: M01-M05 ok. Workspace Databricks Premium, Storage com bronze/silver/gold, pelo menos uma tabela Delta no Silver. Objetivo: habilitar Unity Catalog, criar metastore, catalogs, schemas, external locations, registrar suas tabelas, configurar grants e ver lineage.

Por que Unity Catalog

Antes do UC, cada workspace tinha seu próprio Hive Metastore. Permissões eram em nível de cluster (“Table ACL”). Lineage não existia.

Com UC:

  • 1 metastore por região, compartilhado entre workspaces.
  • Modelo 3-níveis: catalog.schema.table (vs 2-níveis antigo schema.table).
  • Grants centralizados (GRANT SELECT ON TABLE catalog.schema.tabela TO grupo).
  • Lineage automática (de coluna a coluna).
  • Audit logs com quem leu o quê.
  • Volumes (governança para arquivos não-tabulares).
  • External locations (caminhos ADLS governados centralmente).

Arquitetura

ACCOUNT (sua conta Databricks corporativa)
└─ METASTORE (1 por região)
├─ CATALOG bronze_dev
│ ├─ SCHEMA nyctaxi
│ │ └─ TABLE yellow_trips_raw
│ └─ SCHEMA adventureworks
├─ CATALOG silver_dev
├─ CATALOG gold_dev
└─ EXTERNAL LOCATIONS (referência a caminhos ADLS)
├─ ext_loc_bronze ──► abfss://bronze@stdewdevbrs.dfs.core.windows.net/
├─ ext_loc_silver ──► abfss://silver@stdewdevbrs.dfs.core.windows.net/
└─ ext_loc_gold ──► abfss://gold@stdewdevbrs.dfs.core.windows.net/
└─ STORAGE CREDENTIALS (acesso ao storage)
└─ sc_adls_dev (Access Connector for Databricks)

Parte 1 — Verificar / habilitar UC na conta

Em Azure Databricks, a Account Console fica em https://accounts.azuredatabricks.net (precisa de Global Admin ou Databricks Account Admin). Como você é o owner da subscription, é você mesmo.

Passo 1.1 — Acessar Account Console

  1. No workspace → canto superior direito → seu e-mail → Manage account.
  2. Ou abra direto: https://accounts.azuredatabricks.net.

Passo 1.2 — Criar metastore (se ainda não existe)

  1. Menu esquerdo → DataCreate metastore.
  2. Nome: metastore-brazilsouth.
  3. Region: Brazil South.
  4. ADLS Gen2 path para metastore-managed tables: você precisa outro container dedicado (não use bronze/silver/gold). Crie:
Terminal window
az storage container create --account-name $ST --name "uc-metastore" --auth-mode login

Path no campo: abfss://uc-metastore@stdewdevbrs.dfs.core.windows.net/.

  1. Access connector for Azure Databricks: precisa ser criado uma vez — é um recurso Azure que dá identidade gerenciada ao UC.
Terminal window
$AC = "ac-databricks-uc-brs"
az databricks access-connector create `
--resource-group $RG `
--name $AC `
--location $REG `
--identity-type SystemAssigned
# Pega o ID para colar no Account Console:
az databricks access-connector show -g $RG -n $AC --query id -o tsv

Cole o ID em Access connector ID.

  1. Create → atribua ao seu workspace dbw-de-dev-brs.

Passo 1.3 — Dar à MI do Access Connector permissão no storage

Terminal window
$ACMI = az databricks access-connector show -g $RG -n $AC --query identity.principalId -o tsv
$STID = az storage account show -n $ST -g $RG --query id -o tsv
az role assignment create `
--assignee $ACMI `
--role "Storage Blob Data Contributor" `
--scope $STID

Parte 2 — Storage Credential + External Locations

Passo 2.1 — Storage Credential

No workspace UI:

  1. Catalog Explorer (ícone de banco de dados, esquerda).
  2. External DataStorage CredentialsCreate credential.
  3. Nome: sc_adls_dev.
  4. Tipo: Azure Managed Identity.
  5. Access Connector ID: o id do ac-databricks-uc-brs (do passo anterior).
  6. Create.

Ou via SQL:

CREATE STORAGE CREDENTIAL sc_adls_dev
WITH AZURE_MANAGED_IDENTITY (
`access_connector_id` = '/subscriptions/<sub>/resourceGroups/rg-de-dev-brs/providers/Microsoft.Databricks/accessConnectors/ac-databricks-uc-brs'
);

Passo 2.2 — External Locations (3, uma por camada)

Pra cada container, uma external location:

CREATE EXTERNAL LOCATION ext_loc_bronze
URL 'abfss://bronze@stdewdevbrs.dfs.core.windows.net/'
WITH (STORAGE CREDENTIAL sc_adls_dev)
COMMENT 'raw zone';
CREATE EXTERNAL LOCATION ext_loc_silver
URL 'abfss://silver@stdewdevbrs.dfs.core.windows.net/'
WITH (STORAGE CREDENTIAL sc_adls_dev);
CREATE EXTERNAL LOCATION ext_loc_gold
URL 'abfss://gold@stdewdevbrs.dfs.core.windows.net/'
WITH (STORAGE CREDENTIAL sc_adls_dev);

Valida:

SHOW EXTERNAL LOCATIONS;

Parte 3 — Catalogs e Schemas

-- Catalog por camada (ou por domínio — depende da estratégia da empresa)
CREATE CATALOG IF NOT EXISTS bronze_dev MANAGED LOCATION 'abfss://uc-metastore@stdewdevbrs.dfs.core.windows.net/bronze_dev/';
CREATE CATALOG IF NOT EXISTS silver_dev MANAGED LOCATION 'abfss://uc-metastore@stdewdevbrs.dfs.core.windows.net/silver_dev/';
CREATE CATALOG IF NOT EXISTS gold_dev MANAGED LOCATION 'abfss://uc-metastore@stdewdevbrs.dfs.core.windows.net/gold_dev/';
-- Schemas (= bancos)
CREATE SCHEMA IF NOT EXISTS silver_dev.nyctaxi
COMMENT 'Camada Silver - dataset NYC Taxi';
CREATE SCHEMA IF NOT EXISTS silver_dev.adventureworks
COMMENT 'Camada Silver - dataset AdventureWorks';
CREATE SCHEMA IF NOT EXISTS gold_dev.nyctaxi;

Padrão alternativo (mais comum em empresa grande): catalog por domínio de negócio (vendas, marketing, rh), schemas por camada (bronze, silver, gold). Discuta com seu time qual estratégia adotar.

Parte 4 — Registrar a tabela do M04 como External Table

A tabela Silver do M04 foi escrita direto em abfss://silver@.../domain=nyctaxi/yellow_trips/ (sem passar por UC). Vamos “registrá-la”:

CREATE TABLE IF NOT EXISTS silver_dev.nyctaxi.yellow_trips
USING DELTA
LOCATION 'abfss://silver@stdewdevbrs.dfs.core.windows.net/domain=nyctaxi/yellow_trips/';

Valida:

DESCRIBE EXTENDED silver_dev.nyctaxi.yellow_trips;
SELECT COUNT(*) FROM silver_dev.nyctaxi.yellow_trips;
SELECT pickup_date, COUNT(*) qtd FROM silver_dev.nyctaxi.yellow_trips GROUP BY pickup_date ORDER BY pickup_date LIMIT 5;

Managed vs External Tables

ManagedExternal
Quem controla os arquivosUCVocê
DROP TABLE apaga arquivos?SimNão, só desregistra
Caso de usoMaioriaTabelas em paths já existentes ou compartilhados com outras tools

Recomendação: para a trilha, External (já temos os paths). Em greenfield, Managed.

Parte 5 — Adaptar o notebook M04 para usar UC

Substitua a parte final do M04 por:

silver_table = "silver_dev.nyctaxi.yellow_trips"
(
df_dedup
.write
.format("delta")
.mode("overwrite")
.option("overwriteSchema", "true")
.partitionBy("pickup_date")
.saveAsTable(silver_table) # em vez de .save(path)
)
display(spark.table(silver_table).limit(5))

Vantagem: você nunca mais precisa lembrar o path. Lê com spark.table("silver_dev.nyctaxi.yellow_trips") ou via SQL SELECT * FROM silver_dev.nyctaxi.yellow_trips.

Parte 6 — Camada Gold: fato + dim

Vamos criar um exemplo Gold a partir do Silver. Notebook 03_silver_to_gold.py:

# Databricks notebook source
from pyspark.sql import functions as F
silver = spark.table("silver_dev.nyctaxi.yellow_trips")
# COMMAND ----------
# MAGIC %md ## Dim Date
dim_date = (
silver.select(F.col("pickup_date").alias("date_key"))
.distinct()
.withColumn("year", F.year("date_key"))
.withColumn("month", F.month("date_key"))
.withColumn("day", F.dayofmonth("date_key"))
.withColumn("weekday", F.dayofweek("date_key"))
.withColumn("is_weekend", F.col("weekday").isin([1, 7]))
)
dim_date.write.format("delta").mode("overwrite").saveAsTable("gold_dev.nyctaxi.dim_date")
# COMMAND ----------
# MAGIC %md ## Dim Location (vai precisar de lookup externo; aqui só com IDs)
dim_location = (
silver.select(F.col("pu_location_id").alias("location_id")).distinct()
.unionByName(silver.select(F.col("do_location_id").alias("location_id")).distinct())
.distinct()
.orderBy("location_id")
)
dim_location.write.format("delta").mode("overwrite").saveAsTable("gold_dev.nyctaxi.dim_location")
# COMMAND ----------
# MAGIC %md ## Fact Trips (granularidade: 1 linha = 1 corrida)
fact_trips = (
silver
.select(
F.col("pickup_date").alias("date_key"),
F.col("pu_location_id"),
F.col("do_location_id"),
F.col("vendor_id"),
F.col("passenger_count"),
F.col("trip_distance_km"),
F.col("trip_duration_min"),
F.col("fare_amount"),
F.col("tip_amount"),
F.col("total_amount"),
F.col("payment_type"),
)
)
(
fact_trips
.write.format("delta").mode("overwrite")
.partitionBy("date_key")
.saveAsTable("gold_dev.nyctaxi.fact_trips")
)
# COMMAND ----------
# MAGIC %md ## Validação
display(spark.sql("""
SELECT d.year, d.month, COUNT(*) trips, AVG(f.total_amount) avg_total
FROM gold_dev.nyctaxi.fact_trips f
JOIN gold_dev.nyctaxi.dim_date d ON f.date_key = d.date_key
GROUP BY d.year, d.month
ORDER BY 1,2
"""))

Parte 7 — Grants (mínimo viável)

Conceito de hierarquia

Permissões herdam do nível superior:

metastore → catalog → schema → table

Grant em catalog vale pra todos os schemas dentro.

Grants úteis

-- Time de analytics lê tudo do Gold:
GRANT USE CATALOG ON CATALOG gold_dev TO `grp-data-analysts`;
GRANT USE SCHEMA ON SCHEMA gold_dev.nyctaxi TO `grp-data-analysts`;
GRANT SELECT ON SCHEMA gold_dev.nyctaxi TO `grp-data-analysts`;
-- DE escreve em silver/gold:
GRANT USE CATALOG, CREATE SCHEMA ON CATALOG silver_dev TO `grp-data-engineers`;
GRANT ALL PRIVILEGES ON SCHEMA silver_dev.nyctaxi TO `grp-data-engineers`;
-- Cientista vê só uma tabela:
GRANT USE CATALOG ON CATALOG silver_dev TO `cientista@empresa.com`;
GRANT USE SCHEMA ON SCHEMA silver_dev.nyctaxi TO `cientista@empresa.com`;
GRANT SELECT ON TABLE silver_dev.nyctaxi.yellow_trips TO `cientista@empresa.com`;

Ver permissões

SHOW GRANTS ON TABLE silver_dev.nyctaxi.yellow_trips;
SHOW GRANTS `grp-data-analysts`;

Row Filters / Column Masks (avançado, cai em entrevista)

-- Função que retorna TRUE se o usuário pode ver a linha
CREATE FUNCTION mascara_estado(uf STRING)
RETURNS BOOLEAN RETURN
IS_MEMBER('admin') OR uf = 'SP'; -- todo mundo só vê SP, exceto admin
ALTER TABLE silver_dev.clientes.cadastro
SET ROW FILTER mascara_estado ON (uf);
-- Mask: ofusca colunas
CREATE FUNCTION mascara_email(email STRING)
RETURNS STRING RETURN
CASE WHEN IS_MEMBER('admin') THEN email
ELSE regexp_replace(email, '(.+)@', '****@')
END;
ALTER TABLE silver_dev.clientes.cadastro
ALTER COLUMN email SET MASK mascara_email;

Parte 8 — Lineage

UC captura automaticamente. Para ver:

  1. Catalog Explorersilver_devnyctaxiyellow_trips.
  2. Aba Lineage.
  3. Você vê:
    • Upstream: notebooks/jobs que ESCREVERAM nessa tabela.
    • Downstream: o que LEU.
    • Column lineage: quais colunas do gold vieram de quais colunas do silver.

Para lineage aparecer, precisa rodar pelo menos uma vez no cluster UC-enabled após criar a tabela.

Parte 9 — Volumes (governança de arquivos não-tabulares)

Útil para CSV pré-processado, modelos ML, imagens. Substitui dbfs:/ em UC.

CREATE VOLUME silver_dev.nyctaxi.landing
COMMENT 'arquivos brutos antes de virarem tabela';
-- Path acessível:
-- /Volumes/silver_dev/nyctaxi/landing/<arquivo>

Em notebook:

files = dbutils.fs.ls("/Volumes/silver_dev/nyctaxi/landing/")

Pegadinhas

  1. Cluster não-UC (modo No isolation shared) não vê catalogs UC. Garanta data_security_mode: SINGLE_USER ou USER_ISOLATION (M03).
  2. PERMISSION_DENIED em external location: a MI do Access Connector não tem role no storage. Volte ao Passo 1.3.
  3. Confundir Storage Credential com External Location: SC = “como acessa o storage”; EL = “qual caminho está liberado”.
  4. Criar tabela apontando para path fora de external location → falha. EL precisa cobrir o path.
  5. DROP TABLE em managed table apaga os dados! External só desregistra. Cuidado com o tipo.
  6. SHOW GRANTS retorna vazio → você está olhando o nível errado da hierarquia. Confira no nível de catalog/schema também.

Audit logs

Logs de uso ficam em system tables UC:

SELECT * FROM system.access.audit
WHERE event_date >= current_date() - 7
AND service_name = 'unityCatalog'
ORDER BY event_time DESC
LIMIT 100;

Útil pra “quem leu PII na semana passada”.

Checklist de saída

  • Metastore criado e atribuído ao workspace.
  • Access Connector com role Storage Blob Data Contributor no storage.
  • Storage Credential + 3 External Locations (bronze, silver, gold).
  • Catalogs bronze_dev, silver_dev, gold_dev criados.
  • Schema silver_dev.nyctaxi com a tabela yellow_trips.
  • Notebook M04 adaptado para saveAsTable("silver_dev.nyctaxi.yellow_trips").
  • Notebook 03 cria pelo menos fact_trips e dim_date no Gold.
  • Conseguiu ver pelo menos 1 link de lineage.
  • Sabe responder em entrevista: managed vs external, hierarquia de grants, row filter / column mask.

Próximo

M07 — CI/CD com Databricks Asset Bundles + GitHub Actions

← Anterior
M05 Adf Orquestracao
Próximo →
M07 Cicd Dab Github