Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Quando vários notebooks do Fabric, pipelines ou trabalhos do Spark gravam na mesma tabela Delta ao mesmo tempo, o Delta Lake usa o controle de concorrência otimista (OCC) para manter a tabela consistente. Cada transação lê um snapshot, grava novos arquivos e valida se nenhum commit conflitante tenha ocorrido nesse intervalo. Se um conflito for detectado, a transação falhará com uma exceção em vez de corromper dados.
Este artigo aborda padrões práticos para gerenciar operações de gravação simultâneas no Fabric. Para obter uma especificação completa do protocolo OCC, consulte o controle de simultaneidade do Delta Lake (documentação de software livre).
Níveis de isolamento
Todas as tabelas Delta usam o nível de isolamento serializável . Serializável é o nível mais estrito e o único com suporte. Ele garante que o resultado de transações simultâneas seja idêntico a alguma ordem de execução sequencial.
O Delta Lake também usa um nível de SnapshotIsolation interno para operações que não alteram dados lógicos (como OPTIMIZE). SnapshotIsolation ignora a verificação de acréscimo simultâneo, permitindo que a compactação prossiga sem entrar em conflito com inserções simultâneas. Você não configura o SnapshotIsolation diretamente – o Delta Lake aplica-o automaticamente quando apropriado.
Com Serializable o isolamento, um acréscimo cego simultâneo (INSERT INTO) pode entrar em conflito com um MERGE ou UPDATE que lê a mesma partição.
Quais operações entram em conflito
Nem todas as gravações simultâneas entram em conflito. O fator chave é se duas operações tocam nos mesmos arquivos subjacentes.
| Par concorrente | Conflito? | Por que |
|---|---|---|
Duas operações de INSERT anexação |
No | Cada um adiciona novos arquivos sem ler os existentes (anexação às cegas). |
INSERT + OPTIMIZE |
No |
OPTIMIZE faz commit em SnapshotIsolation porque não altera dados lógicos, por isso ignora completamente a verificação de append concorrente. Anexações adicionam novos arquivos que não se sobrepõem aos arquivos que estão sendo compactados. |
Duas operações UPDATE, DELETE ou MERGE |
Sim, se eles lerem ou modificarem arquivos sobrepostos | Cada um reescreve arquivos, portanto o snapshot de quem grava por último está desatualizado. |
OPTIMIZE + UPDATE/DELETE/MERGE |
Sim, se eles tocarem nos mesmos arquivos |
OPTIMIZE remove e readiciona arquivos (com dataChange=false). Se uma operação de modificação de dados também ler esses mesmos arquivos, um ConcurrentDeleteReadException será gerado. |
Duas OPTIMIZE execuções |
Sim, se eles selecionarem os mesmos arquivos | Ambos tentam remover e reescrever o mesmo conjunto de arquivos, causando um ConcurrentDeleteDeleteException. |
INSERT + MERGE/UPDATE/DELETE |
Sim, se a operação de modificação de dados ler a mesma partição | Em Serializable, um acréscimo cego pode entrar em conflito com modificações simultâneas nos dados se a operação leu uma partição na qual o acréscimo gravou. |
Dica
Pipelines de somente acréscimo (INSERT INTO, df.write.mode("append")) são a maneira mais simples de evitar conflitos por completo. Se sua carga de trabalho puder anexar primeiro e reconciliar depois, você eliminará a contenção entre gravações.
Isolar gravadores com particionamento
A maneira mais comum de executar DML simultâneo na mesma tabela sem conflitos é particionar a tabela pela coluna que separa seus gravadores e, em seguida, incluir essa coluna em cada condição de operação. Quando cada gravador tem como destino uma partição diferente, as operações tocam conjuntos de arquivos desarticulados e não entram em conflito.
Um cenário típico: vários pipelines cada um processa dados para uma unidade de negócios ou locatário diferente. Particione por essa dimensão e fixe o MERGE de cada pipeline na sua partição.
-- Each pipeline targets its own partition, so concurrent runs don't conflict
MERGE INTO events AS target
USING staged AS source
ON target.event_id = source.event_id
AND target.business_unit = 'EMEA'
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *
Importante
A coluna de partição deve aparecer na própria condição de mesclagem, não apenas nos dados de origem. Sem ele, o Delta Lake não pode determinar no momento da validação que as duas operações tocaram conjuntos de arquivos desarticulados e o verificador de conflitos trata a operação como uma leitura de tabela completa.
Para obter mais detalhes sobre estratégias de particionamento, consulte Particionamento para tabelas Delta.
Nova tentativa de commit integrada
O Delta Lake tenta automaticamente uma confirmação quando detecta que outra transação foi confirmada primeiro. Em cada repetição, ele lê a confirmação vencedora, executa o verificador de conflitos e, se não houver nenhum conflito lógico, reattempta a confirmação na próxima versão disponível. Esse processo é repetido de forma transparente sem nenhuma ação do código.
Um conflito lógico (por exemplo, duas operações reescrevendo o mesmo arquivo) não pode ser resolvido automaticamente. A repetição gera uma das exceções listadas em exceções de conflito comuns. No entanto, muitos conflitos transitórios de versão — como dois acréscimos sem verificação prévia disputando a mesma posição de versão — são resolvidos automaticamente e nunca chegam à sua aplicação.
Exceções de conflito comuns
Quando um conflito é detectado, o Delta Lake gera uma exceção específica. Entender qual exceção você vê ajuda a identificar a causa raiz.
| Exceção | O que aconteceu |
|---|---|
ConcurrentAppendException |
Outro gravador anexou arquivos em uma partição (ou conjunto de arquivos) que sua operação estava lendo. Comum quando MERGE é executado em uma partição que também está recebendo inserções de outro pipeline. No nível de isolamento Serializable, até mesmo anexações cegas (operações INSERT simples) podem acionar essa exceção. |
ConcurrentDeleteReadException |
Outro gravador excluiu ou reescreveu um arquivo que sua operação leu. Típico quando OPTIMIZE compacta arquivos que um simultâneo UPDATE ou MERGE também estava lendo ou quando duas operações de modificação de dados se sobrepõem nas mesmas linhas. |
ConcurrentDeleteDeleteException |
Ambas as operações tentaram excluir ou reescrever o mesmo arquivo. Geralmente causado por execuções sobrepostas OPTIMIZE ou por dois pipelines reescrevendo a mesma partição simultaneamente. |
ConcurrentWriteException |
Um conflito genérico gerado quando outra transação foi confirmada para a mesma versão da tabela antes que a resolução de conflitos pudesse ser executada, por exemplo, durante uma atualização de sistema de arquivos para commits gerenciados. |
MetadataChangedException |
O esquema ou as propriedades da tabela foram alterados durante a transação — por exemplo, por uma operação de gravação simultânea ALTER TABLE ou por uma gravação de evolução do esquema. |
ConcurrentTransactionException |
Duas consultas de Streaming Estruturado com o mesmo local de ponto de verificação foram criadas na tabela ao mesmo tempo. Elimine duplicações nas suas tarefas de streaming ou use caminhos de checkpoint distintos. |
ProtocolChangedException |
Uma transação concorrente atualizou ou rebaixou o protocolo da tabela enquanto a transação atual também tentava alterar o protocolo. Também pode ocorrer quando um recurso de tabela é descartado simultaneamente. |
Estratégias comuns para evitar conflitos de gravação
Habilitar a compactação automática
A compactação automática é executada de forma síncrona como parte das operações de gravação. A compactação síncrona impede que tarefas de compactação agendadas separadamente coincidam com operações de modificação de dados, evitando, assim, exceções de gravadores concorrentes.
Agendar manutenção fora das janelas de gravação
OPTIMIZE e VACUUM pode entrar em conflito com operações simultâneas de modificação de dados. No Fabric, agende tarefas de notebook ou atividades de pipeline para compactação de tabelas e VACUUM durante janelas de baixa atividade, por exemplo, após a conclusão da ingestão noturna, em vez de durante ela.
Usar padrões de acréscimo + mesclagem
Para ingestão com alta simultaneidade, grave os dados brutos com gravações somente de acréscimo em uma tabela de staging (sem possibilidade de conflitos) e, em seguida, execute um único trabalho MERGE para reconciliar os dados na tabela de destino. O padrão serializa a operação propensa a conflitos, mantendo a ingestão totalmente paralela.
Adicionar lógica de repetição para conflitos lógicos
A nova tentativa de commit integrada lida automaticamente com conflitos transitórios de versão, mas conflitos lógicos — quando duas operações realmente se sobrepõem — resultam em uma exceção. Como o Delta Lake nunca produz gravações parciais, uma transação com falha pode ser repetida com segurança no nível da aplicação. Para pipelines em que conflitos lógicos ocasionais são esperados, encapsule a gravação na lógica de repetição:
from delta.exceptions import ConcurrentAppendException
import time
# Retry with backoff on transient concurrent write conflicts
max_retries = 3
for attempt in range(max_retries):
try:
spark.sql("MERGE INTO target USING source ON ...")
break
except ConcurrentAppendException:
if attempt < max_retries - 1:
time.sleep(2 ** attempt)
else:
raise
Escolha a estratégia de layout certa
O clustering e o particionamento líquidos resolvem problemas diferentes. O clustering líquido otimiza o layout do arquivo para o desempenho de leitura. O particionamento cria limites físicos que impedem conflitos entre operações de gravação simultâneas. Se sua carga de trabalho precisar de ambos, particione pela coluna de isolamento do gravador e use Z-Order em cada partição para desempenho de leitura.