(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisboa

Como criar partições dinâmicas em tabelas Delta no Lakehouse

João Barros 09 de October de 2026 6 min de leitura

Este tutorial mostra como criar partições dinâmicas numa tabela Delta no Lakehouse para melhorar a leitura e escrita de dados. Particionar correctamente reduz custos de I/O, acelera consultas e evita ficheiros pequenos quando se escreve em paralelo. Além disso, partições bem definidas facilitam operações de limpeza e retenção e ajudam o engine a aplicar partition pruning, o que pode reduzir o volume lido em 10x a 100x em cenários comuns.

Pré-requisitos

  • Conta com acesso ao Microsoft Fabric e um Lakehouse criado.
  • Permissões para criar/alterar tabelas Delta no Lakehouse.
  • Ficheiros de exemplo (CSV/JSON) carregados num path do Lakehouse ou OneLake.
  • Noções básicas de SQL e PySpark.

Passo 1: Escolher a coluna de partição certa

Porquê: uma boa coluna de partição equilibra o número de ficheiros e a selectividade das consultas. Evite colunas com alta cardinalidade (ex.: ID único com milhões de valores) — isso cria muitas pequenas partições e aumenta a sobrecarga de metadados. Também não use colunas com cardinalidade muito baixa (ex.: um campo com sempre o mesmo valor), porque aí não há benefício. Para dados temporais, usar uma hierarquia como year/month[/day] é uma prática comum. Por exemplo:

  • Datas: year/month — bom equilíbrio para anos com ~12 meses e centenas de milhares a milhões de registos por mês.
  • Alta cardinalidade: user_id com 10M de utilizadores — não usar como partição.
  • Baixa cardinalidade: country com 5-10 valores — não suficiente para redução significativa de I/O.

Regra prática: cada partição deve conter pelo menos dezenas de MB a centenas de MB (ex.: 100-500 MB) para ser eficiente com ficheiros Delta. Se as suas partições ficarem consistentemente abaixo de ~64 MB, pense em reduzir a granularidade das partições.

Passo 2: Criar uma tabela Delta não particionada (opcional, para importar dados iniciais)

É útil começar sem partição para validar esquema e depois converter para particionado quando tiver carga de dados real. Isto ajuda a verificar tipos e nulos com dados reais antes de definir partições definitivas. Exemplo em SQL no SQL Analytics endpoint ou notebook:

CREATE TABLE IF NOT EXISTS my_db.raw_events_delta
USING DELTA
LOCATION 'lakehouse:////raw/events'
AS SELECT * FROM csv.`/paths/events/*.csv`;

Depois de importar alguns milhões de linhas, inspecione amostras e observe a distribuição temporal para decidir se usa year/month/day.

Passo 3: Criar tabela Delta particionada com schema final

Crie a tabela com PARTITIONED BY. Aqui usamos year e month extraídos da coluna event_timestamp. Isto cria uma estrutura de pastas organizada, por exemplo /year=2024/month=06/, que facilita pruning e gestão de dados.

CREATE TABLE IF NOT EXISTS my_db.events_delta
(
  event_id STRING,
  event_timestamp TIMESTAMP,
  user_id STRING,
  event_type STRING,
  payload STRING
)
USING DELTA
PARTITIONED BY (year, month)
LOCATION 'lakehouse:////tables/events';

Nota: escolher PARTITIONED BY evita a necessidade de criar colunas físicas separadas para year e month se preferir extraí-las no SELECT, mas ter colunas explícitas facilita queries e índices.

Passo 4: Escrever dados usando partições dinâmicas com PySpark

Ao escrever, calcule year e month e use write.partitionBy para criar partições dinâmicas. Isto garante que os ficheiros são gravados na pasta correcta conforme a data do evento. Exemplo PySpark minimal:

from pyspark.sql.functions import year, month, to_timestamp

df = spark.read.csv('/mnt/lake/events/*.csv', header=True)
df = df.withColumn('event_timestamp', to_timestamp(df.event_timestamp))
df = df.withColumn('year', year(df.event_timestamp))
df = df.withColumn('month', month(df.event_timestamp))

(df.write
  .format('delta')
  .mode('append')
  .partitionBy('year','month')
  .save('lakehouse:////tables/events'))

Exemplo concreto: se tiver 10M de registos por mês e um cluster com 200 executores, estime os ficheiros finais por partição e ajuste repartition para atingir ~128 MB por ficheiro.

Passo 5: Evitar ficheiros pequenos (small files) ao escrever em paralelo

Erro comum: cada executor escreve poucos MB, criando centenas de ficheiros pequenos por partição, o que degrada o desempenho. Soluções:

  • Aumentar o tamanho dos ficheiros com coalesce/repartition antes de escrever: por exemplo, se pretende ficheiros ~128 MB e tem 1 TB de dados, estime 8000 ficheiros e gere repartition(8000).
  • Reparticionar por uma chave de partição lógica (ex.: concat year-month) para agrupar dados da mesma partição antes do write.
  • Executar OPTIMIZE / compaction depois da ingestão para combinar ficheiros pequenos (quando suportado no ambiente).
# Reparticionar por partição aproximada antes de escrever
from pyspark.sql.functions import concat_ws

# Criar uma coluna de partition_key para reparticionar
df = df.withColumn('partition_key', concat_ws('-', df.year.cast('string'), df.month.cast('string')))
# Repartition por partition_key para reduzir ficheiros
df = df.repartition('partition_key')

(df.drop('partition_key')
  .write
  .format('delta')
  .mode('append')
  .partitionBy('year','month')
  .save('lakehouse:////tables/events'))

Passo 6: Atualizar esquema ou adicionar partições

Se adicionar novas colunas, use ALTER TABLE para evitar reescrever toda a tabela. Para detectar novas pastas/partições externas, alguns ambientes suportam comandos como MSCK REPAIR; outros oferecem SHOW PARTITIONS. Exemplos:

ALTER TABLE my_db.events_delta ADD COLUMNS (device STRING);
-- Listar partições
SHOW PARTITIONS my_db.events_delta;

Se for necessário migrar de não particionado para particionado com milhões de linhas, faça uma escrita controlada em batch para evitar picos de I/O.

Verificar o resultado

Confirme que as partições existem e que queries fazem partition pruning. Execute:

-- Ver partições físicas
SHOW PARTITIONS my_db.events_delta;

-- Teste de partition pruning (verifica planos)
EXPLAIN SELECT * FROM my_db.events_delta WHERE year = 2024 AND month = 6;

-- Contar por partição
SELECT year, month, COUNT(*) FROM my_db.events_delta GROUP BY year, month ORDER BY year, month;

Se EXPLAIN mostrar que apenas os caminhos year=2024/month=6 são lidos, o pruning está a funcionar. Monitorize tempos de consulta e I/O: idealmente verá redução significativa quando o filtro corresponder a partições.

Conclusão

Partições dinâmicas em tabelas Delta no Lakehouse melhoram o desempenho e o custo quando bem escolhidas. Comece por uma granularidade conservadora (year/month), monitorize a distribuição de dados e o tamanho médio dos ficheiros e ajuste repartition na escrita. Próximos passos: automatizar partição em pipelines de ingestão, usar OPTIMIZE e ZORDER (quando suportado) para melhorar a ordenação interna e reduzir a latência de leitura, e criar alertas para detectar crescimento de ficheiros pequenos. Dica prática: vise ficheiros de ~128–256 MB por partição e reveja a estratégia a cada trimestre conforme o crescimento dos dados.