Logo

Transações e Autocommit

O impacto do commit automático na integridade e no desempenho.

O que é o Autocommit?

A maioria das suítes de conexão ao banco de dados para Delphi ou Lazarus, como FireDAC, ZeosDBO, SQLdb etc., ao se conectar a uma base de dados, possui uma propriedade chamada autocommit. Ela pode estar ligada (true) ou desligada (false).

Para explicar como funciona, precisamos entender o que é uma transação. Neste contexto, uma transação é um período em que iniciamos uma série de modificações no banco de dados, que deve ser encerrada com um commit — a confirmação de que tudo o que foi feito desde o início da transação deve ser mantido — ou com um rollback — desfazer toda a transação desde o seu início.

Então, o que o autocommit faz ao estar ligado é tornar “iniciar uma transação” opcional ao programador. Porém, por baixo dos panos, a suíte de acesso a dados criará uma transação para você antes de executar o seu statement e a finalizará com um commit. Isso dá ao programador a impressão de que, a partir de agora, não precisará mais se preocupar com iniciar e finalizar transações.

O Problema da Atomicidade

Imagine uma transferência bancária: você precisa tirar dinheiro de uma conta e colocar em outra. No modo Autocommit, se o sistema falhar entre as duas operações, o dinheiro "desaparece". Sem o controle manual de transação, você perde a Atomicidade (o "A" do ACID).

Impacto na Performance

Escrever no disco é caro. No modo Autocommit, o banco é forçado a gravar fisicamente o log de transação a cada comando. Ao agrupar 1000 inserções em uma única transação manual, a performance pode ser até 10x superior.

No vídeo abaixo, eu demonstro que uma transferência de 10.000 registros com autocommit ligado e desligado tem diferenças absurdas de tempo. Por quê? Porque, com o autocommit desligado, você inicia uma transação uma única vez, faz a transferência de 10.000 registros e, depois, dá commit. Porém, com o autocommit ligado, o que acontece de diferente é: inicia uma transação, transfere um registro e, então, dá commit; depois repete a mesma operação com o segundo, o terceiro... até o último registro, executando 10.000 transações. Como resultado, o autocommit ligado torna as coisas muito, mas muito lentas quando as operações são feitas em lote.

-- EVITE O AUTOCOMMIT EM OPERAÇÕES EM LOTE
START TRANSACTION;
INSERT INTO VENDAS ...;
INSERT INTO ITENS_VENDA ...;
-- Se tudo correr bem:
COMMIT;
-- Se algo falhar:
ROLLBACK;

StartTransaction: A Válvula de Escape

Algo importante a se dizer é que o autocommit ligado em ferramentas como Zeos ou FireDAC tem uma válvula de escape: se você der um StartTransaction, a suíte entende que, para esta operação, o autocommit será falso, e você terá de terminar manualmente a transação usando commit (ou rollback). Isso é muito bom para o programador, porque ele contorna possíveis problemas de não ter iniciado uma transação e, caso inicie, terá máximo desempenho por escolher onde dar o commit.

ATENÇÃO: Comportamento do StartTransaction

No Zeos e no FireDAC, quando você chama conn.StartTransaction, a suíte interrompe o autocommit até que ocorra um commit/rollback explícito. Mas atenção: se o seu fluxo de transação não terminar em commit/rollback, vai dar um xabú que não tem tamanho, pois a transação vai encapsular tudo o que vier depois.

Quando você vir uma história de alguém dizendo que o sistema “perdeu tudo” no fim do dia ou que registros estão sumindo, geralmente é disso que estamos falando. A transação começou e, de início, não houve commit nem rollback... daí os dados foram acontecendo depois e, em outro ponto do programa, um try...except deu um rollback, mas não voltou apenas naquele ponto: voltou desde o primeiro StartTransaction que não teve commit/rollback.

Por isso, try..except para começar e terminar uma transação é mandatório: se a transação começou, ninguém sai do bloco com ela ativa (sem commit/rollback).

Exemplo Correto: Usando Try..Except

Veja como deve ser feito corretamente:

// Exemplo com Zeos (padrão recomendado)
// - Não engole a exceção (mantém contexto)
// - Garante que, SE a transação iniciou, ela termina em Commit/Rollback
ZConnection1.StartTransaction;
try
  // Suas operações aqui
  Query1.ExecSQL;
  Query2.ExecSQL;

  // Se tudo correr bem
  ZConnection1.Commit;
except
  // Se algo der errado, desfaz somente o que está nessa transação
  if ZConnection1.InTransaction then
    ZConnection1.Rollback;
  raise; // Relança a exceção para ser tratada fora
end;
// Exemplo com FireDAC (padrão recomendado)
// - Não engole a exceção (mantém contexto)
// - Garante que, SE a transação iniciou, ela termina em Commit/Rollback
FDConnection1.StartTransaction;
try
  // Suas operações aqui
  FDQuery1.ExecSQL;
  FDQuery2.ExecSQL;

  // Se tudo correr bem
  FDConnection1.Commit;
except
  // Se algo der errado, desfaz somente o que está nessa transação
  if FDConnection1.InTransaction then
    FDConnection1.Rollback;
  raise; // Relança a exceção para ser tratada fora
end;
🚨 Erro Comum: Transação Aberta Sem Fechamento

Nunca faça isso:

// ERRADO - NUNCA FAÇA ISSO!
ZConnection1.StartTransaction;
Query1.ExecSQL;
// Se algo der errado aqui, a transação fica aberta!
Query2.ExecSQL;
ZConnection1.Commit;

Se Query2.ExecSQL lançar uma exceção, o Commit nunca será executado e a transação ficará aberta, encapsulando todas as operações subsequentes até que a conexão seja fechada ou um rollback seja executado em outro ponto do código (possivelmente causando perda de dados legítimos).

Por que é importante saber disso?

Por que é importante conhecer a suíte de acesso a banco de dados e suas propriedades, como o autocommit? Porque, em situações de lentidão no acesso à base de dados, a culpa vira “o servidor está lento” ou “o banco de dados é lento”, quando, na realidade, é desconhecimento da ferramenta.

Conclusão

O Autocommit é aceitável para consultas rápidas (SELECT) ou testes simples via terminal. Para aplicações reais, o controle transacional explícito é o que separa um sistema amador de um software corporativo robusto.

Lembre-se sempre: Quando usar StartTransaction, sempre envolva em try..except garantindo que, se a transação estiver ativa, haverá Commit ou Rollback. Isso evita problemas catastróficos de perda de dados ou transações órfãs que podem afetar todo o sistema.

← Voltar para SGBD