Como resolver o problema de deadlock em aplicações Java com PostgreSQL

Como resolver o problema de deadlock em aplicações Java com PostgreSQL

Eis que o log da sua aplicação, num determinado INSERT, por exemplo em Java, mostra o seguinte erro:

org.postgresql.util.PSQLException:
ERROR: deadlock detected

Trata-se do famoso e conhecido “deadlock”,mas aqui falaremos do deadlock verdadeiro, pois existe também um falso deadlock: se os locks em uma tabela são intensos de muitas transações e sub-transações, e o parâmetro deadlock_timeout for igual a 1 segundo, então estão ocorrendo muitos deadlocks detectados pelo PostgreSQL.POr isso então é provável que se tenha um falso deadlock.

Isso se explica pelo fato de que o banco tem muito pouco tempo para determinar se existe mesmo um deadlock (apenas 1s), e então ele opta por ser um deadlock. Assim mata/derruba todas as transações que estão presas e “lockando” determinados registros uma tabela específica.

O parâmetro deadlock_timeout deve ser configurado com o valor de pelo menos 5s, em média, o qual é um tempo suficiente para que o PostgreSQL possa saber de fato se existe ou não existe deadlocks verdadeiros – este é um ponto crucial para continuarmos a discussão, para que ninguém caia nos golpes dos falsos deadlocks.

Vamos então aos deadlocks verdadeiros, uma vez que já sabemos proceder e alterar o deadlock_timeout, caso seja o problema já mencionado de falsos deadlocks. Num deadlock verdadeiro, por exemplo de instruções INSERT e UPDATE, com certeza existe um problema de ordem na inserção de um índice único e exclusivo, visto que muitas seções estão tentando fazer várias alterações com os mesmos valores da chave única, ou com os mesmos valores de um determinado campo da tabela que normalmente possui uma constraint.

Primeiramente, temos que descobrir a condição de deadlock, e se ela for rara, é melhor que você tenha os logs, tanto do servidor de aplicação quanto o de banco de dados, escrevendo tudo em disco para que seja mais fácil a detecção.

Do contrário você entrará na famosa “sinuca de bico”, ou seja, partir para um debug integrado com várias seções para exaustivamente encontrar “a danada” que está acabando com o andamento do seu sistema. Vamos entender um exemplo mais a frente, como simular um deadlock, mas vale lembrar que o compromisso aqui, não é o de detectar, e sim de mostrar algum caminho para a resolução.

Uma das maneiras de se tratar um deadlock seria construir, na aplicação, um mecanismo qualquer que deverá efetuar uma nova tentativa de realizar a alteração no database, após aguardar um certo tempo aleatório, ou seja, depois de um certo tempo, reenviar a transação (dentro de uma function, ou até mesmo um loop, etc), o que não é bem uma estratégia muito boa.

É evidente que, se através dos logs, ou mesmo do debug da aplicação, você descobre com facilidade onde está o problema, então é muito fácil corrigi-lo na própria aplicação.

Transações em conflito devem ser tratadas assim, com mecanismos de espera para reenvio de transações, quando não existe outra possibilidade devido a, por exemplo, regra de negócio. A espera de um certo intervalo de tempo é extremamente necessário para evitar as transações em conflito, principalmente os conhecidos bloqueios ativos, que são os piores casos de deadlocks, e os mais difíceis de se detectar até mesmo através de debug. E o que seriam então estes mecanismos de atrasos para reenvio de transações? A conhecida solução para uma serialização de transações bem-sucedida.

Bem, então vamos a um exemplo de como provocar um deadlock utilizando uma tabela, e o próprio psql. Então, com duas seções do psql conectadas no mesmo database, tente simular as 2 transações sem fim, a seguir:

  • 1 – conexão via psql (seção 1) e conexão no database tranco, de teste;
  • 2 – criação da tabela test02 (figura 1 abaixo);
  • 3 – criação o índice pelo campo cpf (figura 2 abaixo);
  • 4 – criação da outra seção do psql (seção 2);
  • 5 – execução das statements SQL conforme timeline da figura 3.

Figura 1 – Create table test02

 

Figura 2 – Create da PK de test02

Figura 3 – Simulando um dadlock com INSERTs. (OBS:  Tente alterar as instruções montando o seu próprio exemplo de deadlock – troque INSERTs por UPDATEs, e etc).

E como se livrar do deadlock? A melhor maneira seria garantir que nas transações, os bloqueios obtidos sejam totais (de todas as linhas que serão tratadas na mesma), fazendo isto com a statement sql SELECT FOR UPDATE.

Com o SELECT FOR UPDATE,  a garantia de se livrar de deadlocks é mais absoluta, e a coordenação de locks do PostgreSQL nunca derrubará a transação por deadlock. Se o seu conjunto transacional é complexo, alterando várias linhas de várias tabelas simultaneamente, então altere suas transações para utilizar SELECT FOR UPDATE, na ordem em que figuram as chaves primárias e constraints únicas, provocando a serialização nas alterações e,  garantindo o sucesso de todas as transações.

 

Líder em Treinamento e serviços de Consultoria, Suporte e Implantação para o mundo open source. Conheça nossas soluções:

CURSOSCONSULTORIA

Anterior Descubra a união poderosa entre DevOps e Linux para otimizar processos de TI
Próxima Terraform e Gitlab-CI: Como gerenciar sua infraestrutura em nuvem

About author

Você pode gostar também

Infraestrutura TI

Entenda o Processo de Consulta SQL no Sistema Gerenciador de Banco de Dados Postgres

Sempre que utilizamos o Sistema Gerenciador de Banco de Dados (SGBD) Postgres ou PostgreSQL, o foco principal de sua utilização é a realização de consultas (Querys), afinal de nada adiantaria

Containers

Guia Prático: Instalação e Configuração do pgAdmin4 via Docker

O pgAdmin4 é a plataforma de administração e desenvolvimento open source mais popular para PostgreSQL. Neste post, vamos instalar e configurar esta ferramenta via Docker. DOCKER Compartilhe este post: Share

Banco de Dados

Acesso a dados SQL Server através do PostgreSQL: um guia prático

Tenho um PostgreSQL e preciso acessar dados que estão no SQL Server! E agora?! Não! Não precisa entrar em pânico! Existe uma solução para isso. Digamos que em um determinado