Clique aqui para ler esse artigo em PDF.imagem_pdf.jpg

capaSQL12.JPG

Clique aqui para ler todos os artigos desta edição

Transações no PostgreSQL - Locks e Bloqueios

 

Na primeira parte deste artigo, vimos o que é o modelo MVCC e como ele é utilizado pelo PostgreSQL para o controle de transações. Este artigo comenta a forma como o PostGreSQL utiliza Locks e Bloqueios e como estes se comportam em relação ao modelo MVCC. Veremos como obter os Locks por meio de comandos SQL (o que possibilita realizar algumas operações exclusivas nos dados bloqueados para a seção corrente) e como o PostgreSQL, por intermédio do MVCC, cria um Lock de forma inteligente com o objetivo de evitar conflitos de operações entre usuários que acessam os mesmo dados.

Os Locks podem ser definidos como bloqueios executados em objetos ou dados no banco de dados. Esses bloqueios podem ser gerados automaticamente (em função do gerenciamento Multiversão/Multiusuário (MVCC) do PostgreSQL) e manualmente (com comandos ou operações que necessitem “travar” uma tabela ou alguns de seus registros).

 

Modos de Bloqueio

O PostgreSQL trabalha com diversos tipos ou modos de bloqueios diretamente vinculados a ações específicas ao programa para controlar o acesso simultâneo aos dados e tabelas. Esses modos podem ser obtidos de forma automática com o controle de transações (Begin, Commit e Rollback) no MVCC, ou de forma explícita, nas situações em que o modelo multiversão não provê um bloqueio adequado.

Vale ressaltar que o modelo MVCC impede que as operações de leitura bloqueiem operações de escrita, e vice-versa. No entanto, as operações que alteram a estrutura da tabela precisarão bloquear qualquer operação de leitura/escrita concomitante. Para garantir a integridade dos dados durante a alteração da estrutura da tabela, o PostgreSQL aplica bloqueios no nível de tabela de forma automática.

 

Bloqueios no nível de tabela

A Tabela 1 apresenta os modos de bloqueio disponíveis e os contextos nos quais esses modos são usados automaticamente pelo PostgreSQL. Vale ressaltar que todos os bloqueios listados são feitos no nível de tabela, mesmo que o nome contenha a palavra “row” (linha).

 

Bloqueio

Descrição

Entra em Conflito Com

ACCESS SHARE

O comando SELECT obtém um bloqueio deste modo nas tabelas referenciadas. Em geral, qualquer comando que apenas leia a tabela (sem modificá-la) obtém este modo de bloqueio.

ACCESS EXCLUSIVE

ROW SHARE

O comando SELECT FOR UPDATE obtém o bloqueio neste modo na(s) tabela(s) de destino.

EXCLUSIVE e ACCESS EXCLUSIVE

ROW EXCLUSIVE

Os comandos UPDATE, DELETE e INSERT obtêm este modo de bloqueio na tabela de destino (além do modo de bloqueio ACCESS SHARE nas outras tabelas referenciadas). Em geral, este modo de bloqueio é obtido por todos os comandos que modificam os dados da tabela.

SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE

SHARE UPDATE EXCLUSIVE

Obtida pelo comando VACUUM (sem a opção FULL). Protege a tabela contra mudanças simultâneas no esquema durante a execução do comando VACUUM

SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE.

SHARE

Obtido pelo comando CREATE INDEX.

ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE.

SHARE ROW EXCLUSIVE

Este modo de bloqueio não é obtido automaticamente por nenhum comando do PostgreSQL.

ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE

EXCLUSIVE

Este modo de bloqueio não é obtido automaticamente por nenhum comando do PostgreSQL.

ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE.

ACCESS EXCLUSIVE

Obtido pelos comandos ALTER TABLE, DROP TABLE e VACUUM FULL. Este é também o modo de bloqueio padrão para o comando LOCK TABLE sem a especificação explícita do modo.

Entra em conflito com todos os modos de bloqueio Este modo garante que a transação que o obteve seja a única que esteja acessando a tabela.

Tabela 1 - Modelos de Bloqueio no Nível de Tabela

 

A finalidade de todos esses bloqueios é a mesma: bloquear a tabela contra alterações em sua estrutura.

A diferença entre os dois modos de bloqueio está no conjunto de modos com o qual cada um deles entra em conflito. Por conflito, entende-se uma operação que não pode ser executada ou um bloqueio que não pode ser gerado porque outro bloqueio já foi obtido. Duas transações não podem obter ao mesmo tempo modos de bloqueio conflitantes na mesma tabela. Alguns modos de bloqueio são essencialmente autoconflitantes (como por exemplo, o ACCESS EXCLUSIVE). Repare que um dos comandos do ACCESS EXCLUSIVE é o “DROP TABLE”, e não é permitido executar um DROP TABLE na mesma tabela por duas transações simultaneamente. Já os modos de bloqueio não-conflitantes podem ser obtidos várias vezes e de forma simultânea por muitas transações (por exemplo, o modo de bloqueio ACCESS SHARE). Uma vez obtido, o modo de bloqueio é mantido até o fim da transação.

 

Nota: O comando SELECT só pode ser bloqueado por ACCESS EXCLUSIVE.

 

Bloqueios no nível de linha

O bloqueio no nível de linha ou registro é obtido automaticamente quando a linha é atualizada ou excluída. Os bloqueios no nível de linha não afetam a consulta aos dados, já que bloqueiam apenas escritas na mesma linha.

Para ‘forçar’ um bloqueio de linha em um comando SELECT, use a palavra-chave FOR UPDATE:

 

SELECT [colunas] FROM tabela [WHERE ...] FOR UPDATE

 

O modelo MVCC impede que bloqueios de leitura afetem operações de escrita, e vice-versa. Com o comando FOR UPDATE, podemos ‘forçar’ um bloqueio de escrita por meio de um comando de leitura. Isso é útil em aplicações que não se adaptam ao modelo MVCC.

O comando SELECT FOR UPDATE modifica as linhas selecionadas ao marcá-las, o que ocasiona escrita no disco e compromete a performance do processo. Sua utilização deve ser estudada e ele só deverá ser implementado se o modelo MVCC não oferecer suporte ao procedimento a ser executado, ou seja, quando for realmente necessário impedir que outras transações simultâneas manipulem os registros.

Veja um exemplo de bloqueio de registros na Listagem 1. Os dois comandos UPDATE só poderão ser executados após o commit efetuado na Sessão 1.

 

Listagem 1 - Utilização de Função Analítica no Lugar de uma View

 

Sessão

Comando

1

BEGIN;

2

BEGIN;

1

SELECT * FROM contas

WHERE codigo > 20 FOR UPDATE;

2

UPDATE contas SET descricao

= ‘A’ WHERE codigo = 27;

2

UPDATE contas SET descricao

= ‘B’ WHERE codigo = 28;

1

commit;

2

commit;

 

Impasses ou DeadLocks

A utilização de bloqueios explícitos pode causar impasses (deadlocks), especialmente quando duas (ou mais) transações mantiverem bloqueios desejados pelas demais. Nessa situação, nenhuma das transações poderá continuar (observe a Tabela 2). O PostgreSQL detecta automaticamente as situações de impasse e, para solucionálas, aborta uma das transações envolvidas e permite que a(s) outra(s) prossiga(m).

 

Seção 1 bloqueia a Tabela A

 

Seção 2 bloqueia a Tabela B

Seção 1 tenta bloquear a Tabela B

Lock da Seção 1 pois a Tabela B está bloqueadapela Seção 2

Seção 2 tenta bloquear a Tabela A

Lock da Seção 2 pois a Tabela A está bloqueadapela Seção 1

DeadLock       

Tabela 2 - Exemplo de Ocorrência de DeadLock

 

Geralmente, a melhor maneira de se defender contra impasses é evitá-los. Se isto não for possível, você poderá tratálos em tempo de execução, executando novamente as transações abortadas pelos impasses.

 

Como ver os bloqueios

No PostgreSQL podemos visualizar os bloqueios de duas formas:

 

·   Utilizando a ferramenta “ps” do UNIX para obter informações sobre os processos executados (observe na sintaxe da listagem 2 que o ^postgres é um dos parâmetros).

A formatação das informações retornadas pode variar de acordo com o tipo e a versão do UNIX utilizado, mas todas retornarão o PID (Código do Processo) e o que o PostgreSQL está fazendo. Por exemplo, na Listagem 2, é possível identificar que o processo 44494 está executando uma transação. Este processo não está afetando o sistema, mas ele poderia estar impedindo o trabalho dos demais operadores porque uma transação pode causar um Lock Exclusivo e, nesse caso, os demais processos ficariam aguardando a finalização da transação (Waiting).

 

Listagem 2 - Execução do ps do UNIX

 

[serv-sistemas2@postgres ~/data]$ ps -ux | grep -c ^postgres 139

 

[serv-sistemas2@postgres ~/data]$ ps -ux | grep ^postgres

postgres 44570 postmaster: postgres plcp001 10.20.1.138 SELECT (postgres)

postgres 44504 postmaster: postgres plcp001 10.20.1.164 SELECT (postgres)

postgres 44535 postgres plcp001 10.20.0.4 SELECT (postgres)

postgres 44676 postmaster: postgres plcp001 10.20.1.179 idle (postgres)

postgres 44447 postmaster: postgres plcp001 10.20.1.173 idle (postgres)

postgres 45270 postmaster: postgres plcp001 200.186.98.31 idle (postgres)

...

postgres 44494 postmaster: postgres plcp001 10.20.0.15 idle in transaction (postgres)

 

Normalmente, quando uma aplicação cliente é encerrada de forma anormal, como, por exemplo, seu processo é derrubado no Windows pelo Gerenciador de Tarefas, a ação que ela estava executando no banco de dados poderá travar. Para corrigir essa situação, é necessário “matar” o processo PostgreSQL que está vinculado ao cliente, usando o comando KILL.

 

Nota: Não é aconselhável matar um processo do PostgreSQL no UNIX com kill –9. Em vez disso, use sempre kill –TERM. O kill é uma ferramenta do Unix, e o parâmetro -9 termina bruscamente o processo. O Unix simplesmente desaloca a memória do processo, limpa o processo da fila de processamento. O perigo do –9 é que, se em determinado momento o processo estiver realizando uma ação no banco, os dados poderão se perder e o banco de dados será corrompido. O –TERM “pede gentilmente” ao postgreSQL que este se encerre.

 

·   O segundo modo é através de uma view de sistema chamada pg_locks. Essa view lista os bloqueios não encerrados. Tais bloqueios podem ser finalizados com um time-out, com o encerramento da seção de origem ou com o término do processamento (por um COMMIT). A Listagem 3 mostra o resultado da execução de um select nessa view, e a Tabela 3 descreve as colunas obtidas pela execução da view.

 

Listagem 3 - Visualização dos bloqueios ativos

 

select * from pg_locks;

 

relation |

-----------+

database |

-------------+

transaction |

---------------+

pid |

-----+

mode  |

------------------------+

granted

----------

16757 |

|

33360 |

|

|

840 |

1120 |

1120 |

AccessShareLock |

ExclusiveLock |

T

t

 

Coluna

Descrição

relation

É o OID (Object ID) que relaciona o objeto (coluna ou tabela) que recebeu o lock.. Através desta coluna, é possível fazer um relacionamento com o pg_class (que é uma view do catálogo do postgresql) para adquirir maiores informações sobre o objeto de destino.

database

É o OID do banco de dados. Para resgatar o nome do banco, será necessário fazer uma relação deste OID com o pg_database (catálogo do sistema). transaction Identificador (ID) da transação na seção.

transaction

Identificador (ID) da transação na seção.

Pid

Este é o ID do processo PostgreSQL. No Unix, os processos recebem um ID que facilita sua administração pelo administrador do sistema. É fácil relacionar qual é o IP do operador que está causando lock através do comando ps, como vimos no exemplo acima. Se statistic collector estiver ativado no postgresql.conf, será possível fazer um relacionamento com o pg_stat_activity para adquirir mais informações sobre o pid. Mais detalhes a seguir.

mode

Tipo de bloqueio corrente.

granted

Se “t” (true) significa que o lock foi feito, se “f” (false) o processo estará aguardando (waiting) a liberação (possivelmente outro processo fez um bloqueio primeiro).

 

Utilizando o Statistics Collection do PostgreSQL

Para visualizar e manipular os locks no PostgreSQL com mais facilidade, é necessário que o Statistics Collection do PostgreSQL esteja ativo. O Statistics Collection gera uma série de informações estratégicas para o administrador do banco, inclusive informações sobre locks. Para ativá-lo e tirar melhor proveito de seus recursos, execute os seguintes procedimentos:

 

1)           Como o Statistics Collection não é habilitado na instalação do banco, edite o arquivo postgresql.conf alterando (ou adicionando, caso não existam) as seguintes informações:

 

#

#        Access statistics collection

#

stats_start_collector = true

stats_reset_on_server_start = true

stats_command_string = true

stats_row_level = true

stats_block_level = true

 

2)           Faça um reload do postgresql, por exemplo: pg_ctl –D ~postgres/data reload. Este comando fará um reload do banco de dados sem derrubar as conexões e sem prejudicar as operações atuais. Na verdade, ele simplesmente fará com que o postgreSQL leia novamente os arquivos de configuração (postgresql.conf e pg_hba.conf).

 

3)           Crie as seguintes views:

 

CREATE OR REPLACE VIEW “public”.”dba_pid”

(

procpid,

current_query)

AS

SELECT pg_stat_get_backend_

pid(s.backendid) AS procpid,

pg_stat_get_backend_

activity(s.backendid) AS current_query

FROM (

SELECT pg_stat_get_backend_idset() AS

backendid) s;

 

CREATE OR REPLACE VIEW “public”.”dba_locks”

(

pids,

modo,

query)

AS

SELECT DISTINCT dba_pid.procpid AS pids,

l.”mode” AS modo,

dba_pid.current_query AS query

FROM dba_pid, pg_locks l

WHERE (l.pid = dba_pid.procpid)

ORDER BY dba_pid.procpid, l.”mode”,

dba_pid.current_query;

 

Essas views facilitam a visualização de locks e processos. Vejamos o detalhamento das views:

Na View dba_pid, a coluna procpid retorna o ID do processo no sistema operacional e a coluna Current_query corresponde à frase SQL que o operador está executando.

Na View dba_locks a coluna Pids é o ID do processo, Mod indica o modo de lock que o postgreSQL está executando, e Query indica a frase SQL que está causando o lock

A view dba_locks usa a view dba_pid criada anteriormente para buscar as querys e o identificador do processo. A view de sistema pg_locks é utilizada para buscar o nome do modo de bloqueio utilizado.

 

Conclusão

Visando complementar o primeiro artigo, verificamos a utilização de locks automáticos e explícitos e os procedimentos para sua utilização. Esse tópico serve de base para assuntos relacionados a tunning e otimização de processos no servidor de banco de dados. Esses assuntos serão abordados em mais detalhes nas edições futuras. Até lá!