Lock:tuple - Amazon Relational Database Service

Lock:tuple

O evento Lock:tuple ocorre quando um processo de backend está aguardando para adquirir um bloqueio em uma tupla.

Versões compatíveis do mecanismo

Essas informações de eventos de espera são compatíveis com todas as versões do RDS para PostgreSQL.

Contexto

O evento Lock:tuple indica que um backend está aguardando para adquirir um bloqueio em uma tupla enquanto outro backend mantém um bloqueio conflitante na mesma tupla. A tabela a seguir ilustra um cenário em que as sessões geram o evento Lock:tuple.

Tempo

Sessão 1

Sessão 2

Sessão 3

t1

Inicia uma transação.

t2

Atualiza a linha 1.

t3

Atualiza a linha 1. A sessão adquire um bloqueio exclusivo na tupla e aguarda a sessão 1 liberar esse bloqueio via confirmação ou reversão.

t4

Atualiza a linha 1. A sessão aguarda a sessão 2 liberar o bloqueio exclusivo na tupla.

Outra alternativa é simular esse evento de espera utilizando a ferramenta de benchmarking pgbench. Configure um número elevado de sessões simultâneas para atualizar a mesma linha em uma tabela com um arquivo SQL personalizado.

Para saber mais sobre modos de bloqueio conflitantes, consulte o tópico sobre Bloqueio explícito, na documentação do PostgreSQL. Para saber mais sobre pgbench, consulte pgbench na documentação do PostgreSQL.

Possíveis causas do maior número de esperas

Quando esse evento aparece mais que o normal, possivelmente indicando um problema de performance, as causas típicas incluem:

  • Um número elevado de sessões simultâneas está tentando adquirir um bloqueio conflitante para a mesma tupla executando instruções UPDATE ou DELETE.

  • Sessões altamente simultâneas estão executando uma instrução SELECT utilizando os modos de bloqueio FOR UPDATE ou FOR NO KEY UPDATE.

  • Diversos fatores fazem com que grupos de aplicações ou conexões abram mais sessões para executar as mesmas operações. À medida que novas sessões estão tentando modificar as mesmas linhas, a carga de banco de dados pode atingir picos e Lock:tuple pode surgir.

Para obter mais informações, consulte o tópico sobre Bloqueios em nível de linha, na documentação do PostgreSQL.

Ações

Recomenda-se ações distintas, dependendo dos motivos do evento de espera.

Investigar a lógica da sua aplicação

Descubra se uma sessão bloqueadora está no estado idle in transaction por muito tempo. Em caso positivo, considere encerrar a sessão bloqueadora como uma solução de curto prazo. Também é possível utilizar a função pg_terminate_backend. Para obter mais informações sobre essa função, consulte Funções de sinalização de servidor, na documentação do PostgreSQL.

Para uma solução de longo prazo, faça o seguinte:

  • Ajuste a lógica da aplicação.

  • Use o parâmetro idle_in_transaction_session_timeout. Esse parâmetro encerra qualquer sessão com uma transação aberta que tenha ficado ociosa por um tempo maior que o especificado. Para obter mais informações, consulte o tópico sobre Padrões de conexão de clientes, na documentação do PostgreSQL.

  • Use a confirmação automática o máximo possível. Para obter mais informações, consulte SET AUTOCOMMIT, na documentação do PostgreSQL.

Localizar a sessão bloqueadora

Enquanto o evento de espera Lock:tuple está ocorrendo, identifique a sessão bloqueadora e a sessão bloqueada descobrindo quais bloqueios dependem um do outro. Para obter mais informações, consulte o tópico sobre Informações de dependências de bloqueios, na wiki do PostgreSQL.

O exemplo a seguir consulta todas as sessões, filtrando em tuple e ordenando por wait_time.

SELECT blocked_locks.pid AS blocked_pid, blocking_locks.pid AS blocking_pid, blocked_activity.usename AS blocked_user, blocking_activity.usename AS blocking_user, now() - blocked_activity.xact_start AS blocked_transaction_duration, now() - blocking_activity.xact_start AS blocking_transaction_duration, concat(blocked_activity.wait_event_type,':',blocked_activity.wait_event) AS blocked_wait_event, concat(blocking_activity.wait_event_type,':',blocking_activity.wait_event) AS blocking_wait_event, blocked_activity.state AS blocked_state, blocking_activity.state AS blocking_state, blocked_locks.locktype AS blocked_locktype, blocking_locks.locktype AS blocking_locktype, blocked_activity.query AS blocked_statement, blocking_activity.query AS blocking_statement FROM pg_catalog.pg_locks blocked_locks JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid AND blocking_locks.pid != blocked_locks.pid JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid WHERE NOT blocked_locks.GRANTED;

Reduzir a simultaneidade quando ela estiver elevada

O evento Lock:tuple pode ocorrer constantemente, especialmente em um tempo de workload ocupado. Nessa situação, considere reduzir a simultaneidade elevada para linhas muito ocupadas. Várias vezes, apenas algumas linhas controlam uma fila ou a lógica booleana, o que torna essas linhas muito ocupadas.

Você pode reduzir a simultaneidade utilizando diferentes abordagens com base no seu requisito de negócios, na lógica da aplicação e no tipo de workload. Por exemplo, você pode fazer o seguinte:

  • Reformule sua tabela e a lógica de dados para reduzir a simultaneidade elevada.

  • Altere a lógica da aplicação para reduzir a simultaneidade elevada no nível da linha.

  • Aproveite e reformule consultas com bloqueios em nível de linha.

  • Use a cláusula NOWAIT com operações de novas tentativas.

  • Considere utilizar o controle de simultaneidade lógico otimista e bloqueio híbrido.

  • Considere modificar o nível de isolamento do banco de dados.

Solucionar problemas de gargalos

O Lock:tuple pode ocorrer com gargalos, como falta de CPU ou uso máximo da largura de banda do Amazon EBS. Para diminuir gargalos, considere as seguintes abordagens:

  • Aumente a escala do seu tipo de classe de instância na vertical.

  • Otimize consultas que consomem muitos recursos.

  • Altere a lógica da aplicação.

  • Arquive dados acessados raramente.