Pular para o conteúdo principal

Lazarus - AutoCommit no ZeosLib com PostgreSQL

Por padrão, o PostgreSQL trata cada comando SQL como sendo executado dentro de uma transação. Ou seja, cada comando DML (INSERT, UPDATE ou DELETE) tem um comando de início de transação (BEGIN) e um comando COMMIT – caso seja bem sucedido – executados implicitamente antes e depois. Para a execução de múltiplos comandos DML isso leva a um overhead. Desta forma, sugere-se fortemente envolver todos os comandos DML em uma única transação – se a lógica da sua aplicação permitir. Para demonstrar esta recomendação fizemos um teste simples. Escrevemos um pequeno programa para inserir 5.000 linhas em uma tabela. Primeiro fizemos o teste com o controle de transação padrão - cada uma das 5.000 linhas tem seu próprio BEGIN e COMMIT - e depois modificamos o código para controlar a transação manualmente. Neste último teste todas as 5.000 linhas são inseridas dentro de uma única transação.
Para nosso teste usamos ZeosLib. Por padrão o ZeosLib também opera no modo Auto-Commit, ou seja, cada comando é executado no contexto de uma transação. Não mostraremos aqui como conectar ao PG usando Zeos, pois isso já foi apresentado no post Lazarus - Conectando PostgreSQL com ZeosLib. Vejamos o primeiro teste. A unit Dateutils deve ser incluida na cláusula uses para poder usar a função
MilliSecondsBetween(). dbBanco é um ZConnection, dmDados é um Data Module e queCidade uma ZQuery.

var
  i: integer;
  a, b: TDateTime;
begin
  dmDados.queCidade.SQL.Clear;
  dmDados.queCidade.SQL.Add('insert into cidade values (:id, :nome)');
  a := time;
  for i := 1 to 5000 do
  begin
    dmDados.queCidade.ParamByName('ID').Value:= i;
    dmDados.queCidade.ParamByName('NOME').Value:='SANTAREM';
    dmDados.queCidade.ExecSql;
  end;
  b := time;
  ShowMessage(IntToStr(MilliSecondsBetween(b,a)));
end;

Neste primeiro código o tempo médio de inserção das 5.000 linhas foi de 11,6 segundos. Alteramos então nosso código para que todas as linhas fossem inseridas dentro de uma única transação. Para isso chamamos StartTransaction antes de iniciar os INSERTs. Observe o código abaixo.

var
  i: integer;
  a, b: TDateTime;
begin
  a := time;
  dmDados.queCidade.SQL.Clear;
  dmDados.queCidade.SQL.Add('insert into cidade values (:id, :nome)');
  dmDados.dbBanco.StartTransaction;
  for i := 1 to 5000 do
  begin
    dmDados.queCidade.ParamByName('ID').Value:= i;
    dmDados.queCidade.ParamByName('NOME').Value:='SANTAREM';
    dmDados.queCidade.ExecSql;
  end;
  dmDados.dbBanco.Commit;
  b := time;
  ShowMessage(IntToStr(MilliSecondsBetween(b,a)));
end;

Neste caso o tempo médio de execução do código foi de 2,9 segundos. Um ganho de tempo considerável em relação à execução em modo padrão.
Fizemos também um teste usando TZUpdateSQL, implementado de acordo com o post citado acima. O código é apresentado em seguida e o tempo médio de execução foi de 3,8 segundos. Um desempenho um pouco pior do que o teste anterior.

var
  i: integer;
  a, b: TDateTime;
begin
  a := time;
  dmDados.queCidade.Open;
  dmDados.dbBanco.StartTransaction;
  for i := 1 to 5000 do
  begin
    dmDados.queCidade.Insert;
    dmDados.queCidade.FieldByName('ID').Value := i;
    dmDados.queCidade.FieldByName('NOME').Value:='SANTAREM';
    dmDados.queCidade.Post;;
  end;
  dmDados.dbBanco.Commit;
  b := time;
  ShowMessage(IntToStr(MilliSecondsBetween(b,a)));
end;

Estes teste foram feitos em Windows e servidor de BD e cliente rodando na mesma máquina. Certamente se você executar esses testes em outro ambiente, os tempos serão diferentes, mas comparativamente você deverá chegar à mesma conclusão.
Sabe-se que uma falha no sistema, enquanto uma transação estiver ativa, causa a execução de um ROLLBACK pelo SGBD, fazendo com que todas as atualizações feitas a partir do BEGIN sejam canceladas. Portanto, use esta recomendação com cautela e bom senso. Longas transações ativas estão sujeitas a perdas de dados muito maiores que transações curtas, em caso de falhas no sistema.

Atualizado em 28/09/2010.

Comentários

Anônimo disse…
Professor, interessante este artigo sobre transação usando ZeosLib e PGSql.
Estou tentando usar o mesmo conceito usando SQLdb e MySql, porém não tenho sucesso, é como se a transação não tivesse sido inicializada. O sr. sabe me dizer se é possível?
Professor Carlos disse…
No SQLdb tem o SQLTransaction. É ele que chama o StartTransaction e o Commit. Fiz o teste aqui e funcionou.
Anônimo disse…
Ok, Obrigado.
No meu caso estou usando Linux. Você conhece algum caso? A versão de Lazarus que estou usando pode influenciar nessa questão?
Professor Carlos disse…
Estou usando a versão 0.9.29 do Lazarus. E penso que não teria diferença no caso de ser Linux.
Anônimo disse…
Valeu Professor, obrigado pela força.
Anônimo disse…
Professor, quero aproveitar, e parabenizá-lo pela iniciativa. Eu também estou começando a minha aventura com a Lazarus, e vejo bastante futuro com esse. E os seus artigos estão sendo de grande valia para mim, e pretendo no futuro dar minha parcela de colaboração.
Até mais, obrigado.
Professor Carlos disse…
Obrigado pelo comentário. Quanto mais pessoas usarem o Lazarus, maiores serão as chances de seu crescimento.
Anônimo disse…
Oi professor, bom dia!
Me parece que no windows mobile a dll sqllite não é muito confiável, pois
em um mesmo aplicativo, estava funcionando sem problemas, porém sem nenhuma alteração passou a não conectar mais a base de dados.
Teria alguma informação sobre essa situacao?
Professor Carlos disse…
Eu também tive dificuldades com a dll no WM 6. No WM 5 está funcionando bem. Nesse link http://www.parmaja.com/downloads/sqlite3-3.7.0.1-wince-arm.zip
Eles dizem que tem uma nova versão da dll. Seria o caso de fazer testes. Eu não tive tempo de fazer isso.
Rafael Elias disse…
Caro Professor

Em um sistema que desenvolvi, em algumas situações precisei controlar as transações manualmente, pois eu necessitava gravar dois blocos de informações, e caso o segundo falhasse, o primeiro deveria ser revertido. Ótimo, criei uma rotina com TRY EXCEPT END, com StartTransaction, Commit e RollBack e setei o autocommit do ZConnection para false. Porém quando executava a rotina recebia um erro mais ou menos assim: "Cannot perform this operation in non autocommit connection".
Após pesquisar no site do Zeoslib, encontrei a informação que eu precisava. Lá eu li que eu não precisava usar o starttransaction, que este era chamado automaticamente, e que eu precisaria apenas do commit ou rollback. Testei e funcionou perfeito, até simulei um erro para confirmar que o rollback também funcionava, e funcionou.

Uso Lazarus 0.9.31 com Zeos 7.0 ambos do SVN.

Gostaria de aproveitar para parabenizá-lo pelo blog e pela divulgação do Lazarus.

Mantenho um site com informações, tutoriais, artigos, etc. para desenvolvimento de sistemas para WEB com Lazarus (lazarus-cgi.co.cc) todo feito em Lazarus, e gostaria de colocar um link do seu blog por lá. Caso ache a ideia interessante, me informe por favor [é gratis :)].

Abraço

Rafael Tuim Elias
Estava com problemas no desenvolvimento de uma apliação e sua explicação foi de grande valia.

Muito obrigado.
Olá Professor!

Vi nos comentários que você utilizou SQLite com Windows Mobile 5 e 6 (WinCE). Estou desenvolvendo um projeto aberto que utiliza ZeosLib e estou pensando em adicionar suporte a Windows CE a ele. Pergunto: Você utilizou ZeosLib com Lazarus para isso?
Professor Carlos disse…
Oi Fábio

ZeosLib não funcionou para WinCE. Usei TSQLite3Dataset da unit sqlite3ds. Você pode ver como eu fiz em http://professorcarlos.blogspot.com/2010/03/lazarus-criando-uma-aplicacao-para.html. Fiz com DBF também: http://professorcarlos.blogspot.com/2010/08/lazarus-wince-com-dbf.html

Postagens mais visitadas deste blog

Lazarus - Acessando banco de dados com SQLdb - Parte I

Para fazer nossa primeira aplicação usando banco de dados no Lazarus vamos usar o SQLite e o conjunto de componentes nativo SQLdb. Inicialmente vamos apresentar passo como essa aplicação foi criada. Essa foi a maneira que eu fiz, e eu agradeço sugestões e questionamentos que pessoas que já passaram por essa experiência. Depois irei fazer algumas considerações sobre o uso do SQLdb. SQLite SQLite é uma biblioteca que implementa um motor de banco de dados SQL. É livre para qualquer finalidade, seja uso particular ou comercial. Lê e escreve em um único arquivo que pode ter além de tabelas, índices, gatilhos e visões. Executa em várias plataformas e é indicado para aplicações embarcadas. Maiores detalhes podem ser encontrados no site oficial. Para usá-lo, baixe-o do site e faça a instalação adequada para o seu sistema operacional. No Windows isso é muito simples, apenas copie sqlite3.dll para o system32 da pasta do sistema operacional. Existe uma ferramenta de linha de comando chamada

Tipos de dados no SQLite

Em SQLite, diferente de outros motores de banco de dados, o tipo de dado de um valor está associado com o valor propriamente dito, e não com o seu contêiner. É um sistema de tipo dinâmico. Um campo de uma tabela em SQLite pode receber qualquer tipo de dado. Assim, o SQLite simplesmente ignora o tipo informado no comando CREATE TABLE. Então, dizemos que no SQLite existem classes de armazenamento. E essas classes são: NULL - como em qualquer outro banco de dados. INTEGER - inteiro com sinal, armazenado em 1, 2, 3, 4, 6 ou 8 bytes dependendo da grandeza do valor. REAL - valor de ponto flutuante armazenado em 8 bytes. TEXT - uma string armazenada usando UTF-8, UTF-16BE ou UTF-16LE. BLOB - armazena um blob, como indica o nome. Uma coluna INTEGER PRIMARY é uma exceção. Só aceita números inteiros. Qualquer valor em um comando SQL tem uma classe de armazenamento implícita. Durante a execução do comando SQL, o SQLite pode converter valores entre classes numéricas (INTEGER e REAL)

Lazarus - Criando relatórios com FortesReport (Parte I)

Para a criação de relatórios, o Lazarus já trás o componente LazReport, no entanto ele precisa ser instalado no IDE. Para fazer a instalação do pacote, acesse o menu Package -> Open package file (.lpk) . Localize o diretório de instalação do Lazarus e na pasta components abra lazreport e depois source . Abra o pacote lazreport.lpk , clique em Compile e depois em Install . Como já sabemos isso irá recompilar o IDE. Depois de inicializado novamente estará disponível a aba LazReport . Leia aqui um tutorial básico sobre o LazReport. No entanto queremos mostrar uma alternativa ao LazReport . Por essa razão, vamos apresentar neste artigo o FortesReport . Para quem conhece o QuickReport, que fazia parte do Delphi, não terá dificuldade de desenvolver com esse componente. Baixe o pacote aqui e instale. Os procedimentos são semelhantes aos que mostramos acima. Você terá uma nova aba chamada Fortes Report . Conectando o banco de dados O primeiro passo para criar a aplicação é fazer