Deadlocks no PostgreSQL

Deadlocks no 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 Importância do Linux para DevOps
Próxima Participe do maior evento PostgreSQL do Brasil.

About author

Você pode gostar também

Banco de Dados

Mongo DB – Primeiros passos

Caros leitores, hoje vamos tratar de um assunto que anda muito na moda ultimamente. Os bancos de dados conhecidos como não relacionais, ou NoSQL. Longe das brigas com relação à

Banco de Dados

Participe do maior evento PostgreSQL do Brasil.

De volta ao modelo presencial, a PGCONF acontecerá nos dias 26 e 27 de agosto em São José dos Campos/SP. A 4Linux estará presente como uma das patrocinadoras do evento.

Banco de Dados

Saiba o que a parceria entre a 4Linux e a EnterpriseDB representa para o mercad de banco de dados no Brasi.

A EnterpriseDB é uma empresa global especializada na gestão de bancos de dados de missão crítica com tecnologia Open Source. Baseado em PostgreSQL a solução permite um gerenciamento com recursos