9.3. Bloqueio explícito

O PostgreSQL fornece vários modos de bloqueio para controlar o aceso concorrente aos dados nas tabelas. Estes modos podem ser utilizados para o bloqueio controlado pela transação, nas situações onde o MVCC não fornece o comportamento adequado. Também, a maioria dos comandos do PostgreSQL obtém, automaticamente, bloqueios com modos apropriados para garantir que as tabelas referenciadas não serão excluídas ou modificadas de forma incompatível enquanto o comando executa (Por exemplo, o comando ALTER TABLE não pode executar concorrentemente com outras operações na mesma tabela).

9.3.1. Bloqueios no nível de tabela

A lista abaixo mostra os modos de bloqueio disponíveis e os contextos nos quais estes modos são utilizados automaticamente pelo PostgreSQL. Lembre-se que todos estes modos de bloqueio são no nível de tabela, mesmo que o nome contenha a palavra "row" (linha). Os nomes dos modos de bloqueio são históricos. De alguma forma os nomes refletem a utilização típica de cada modo de bloqueio --- mas as semânticas são todas as mesmas. A única diferença real entre um modo de bloqueio e outro é o conjunto de modos de bloqueio com o qual cada um conflita. Duas transações não podem obter modos de bloqueio conflitantes na mesma tabela ao mesmo tempo (Entretanto, uma transação nunca conflita consigo mesma --- por exemplo, pode obter o bloqueio ACCESS EXCLUSIVE e mais tarde obter o bloqueio ACCESS SHARE na mesma tabela). Modos de bloqueio não conflitantes podem ser obtidos concorrentemente por muitas transações. Em particular, deve ser observado que alguns modos de bloqueio são auto-conflitantes (por exemplo, o modo de bloqueio ACCESS EXCLUSIVE não pode ser obtido por mais de uma transação ao mesmo tempo), enquanto outros não são auto-conflitantes (por exemplo, o modo de bloqueio ACCESS SHARE pode ser obtido por várias transações). Uma vez obtido, o modo de bloqueio é mantido até o fim da transação.

Para examinar a lista de bloqueios correntemente mantidos pelo servidor de banco de dados, deve ser utilizada a visão do sistema pg_locks. Para obter mais informações relativas ao monitoramento do status do subsistema de gerência de bloqueios consulte o Guia do Administrador do PostgreSQL.

Modos de bloqueio no nível de tabela

ACCESS SHARE

Conflita apenas com o modo de bloqueio ACCESS EXCLUSIVE.

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

ROW SHARE

Conflita com os modos de bloqueio EXCLUSIVE e ACCESS EXCLUSIVE.

O comando SELECT FOR UPDATE obtém o bloqueio neste modo na(s) tabela(s) de destino (além do bloqueio no modo ACCESS SHARE para as demais tabelas referenciadas mas não selecionadas FOR UPDATE).

ROW EXCLUSIVE

Conflita com os modos de bloqueio SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS 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 UPDATE EXCLUSIVE

Conflita com os modos de bloqueio SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE. Este modo protege a tabela contra mudanças concorrentes no esquema e a execução do comando VACUUM.

Obtida pelo comando VACUUM (sem a opção FULL).

SHARE

Conflita com os modos de bloqueio ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE. Este modo protege a tabela contra mudanças concorrentes nos dados.

Obtido pelo comando CREATE INDEX.

SHARE ROW EXCLUSIVE

Conflita com os modos de bloqueio ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE.

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

EXCLUSIVE

Conflita com os modos de bloqueio ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE. Este modo permite apenas ACCESS SHARE concorrente, ou seja, somente leituras da tabela podem prosseguir em paralelo com uma transação que obteve este modo de bloqueio.

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

ACCESS EXCLUSIVE

Conflita com todos os modos de bloqueio (ACCESS SHARE, ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE e ACCESS EXCLUSIVE). Este modo garante que a transação que o obteve é a única acessando a tabela de qualquer forma.

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.

Nota: Somente o bloqueio ACCESS EXCLUSIVE bloqueia o comando SELECT (sem a cláusula FOR UPDATE).

9.3.2. Bloqueios no nível de linha

Além dos bloqueios no nível de tabela, existem os bloqueios no nível de linha. Um bloqueio no nível de linha, para uma linha específica, é obtido automaticamente quando a linha é atualizada (ou excluída ou marcada para atualização). O bloqueio é mantido até a transação efetivar ou desfazer as alterações. Os bloqueios no nível de linha não afetam a consulta aos dados; bloqueiam apenas escritas na mesma linha. Para obter um bloqueio no nível de linha sem na verdade modificar a linha, deve-se selecionar a linha por meio do comando SELECT FOR UPDATE. Observe que, após um determinado bloqueio ser obtido a transação pode atualizar a linha várias vezes sem que haja conflito.

O PostgreSQL não guarda nenhuma informação em memória relativa às linhas modificadas, portanto não existe limite no número de linhas bloqueadas de uma vez. Entretanto, o bloqueio de uma linha pode causar escrita no disco; por exemplo, o comando SELECT FOR UPDATE modifica as linhas selecionadas para marcá-las ocasionando escrita no disco.

Além dos bloqueios de tabela e de linha, também são utilizados bloqueios no nível de página, compartilhados e exclusivos, para controlar o acesso de leitura e gravação nas páginas da tabela no shared buffer pool. Estes bloqueios são liberados imediatamente após a tupla ser lida ou atualizada. Normalmente os desenvolvedores de aplicação não precisam se preocupar com bloqueios no nível de página, sendo mencionados para o assunto ficar completo.

9.3.3. Impasses

A utilização de bloqueios explícitos pode causar impasses (deadlocks), em particular quando duas (ou mais) transações mantêm bloqueios que outra deseja. Por exemplo, se a transação 1 obtém um bloqueio exclusivo na tabela A e, então, tenta obter um bloqueio exclusivo na tabela B, enquanto a transação 2 já possui um bloqueio exclusivo na tabela B, e agora tenta obter um bloqueio exclusivo na tabela A, então nenhuma das duas transações pode continuar. O PostgreSQL detecta automaticamente as situações de impasse, resolvendo-as abortando uma das transações envolvidas, permitindo que a(s) outra(s) prossiga(m) (Exatamente qual transação é abortada é difícil prever, não se devendo confiar nesta previsão).

Geralmente, a melhor defesa contra os impasses é evitá-los tendo certeza que todas as aplicações que utilizam o banco de dados obtêm estes bloqueios em vários objetos em uma ordem consistente. Deve ser garantido, também, que o primeiro bloqueio de um objeto em uma transação seja aquele com o modo mais elevado que será necessário para este objeto. Se não for possível conhecer esta situação antecipadamente, então os impasses podem ser tratados em tempo de execução tentando novamente a execução das transações abortadas pelos impasses.

Enquanto a situação de impasse não é detectada, uma transação aguardando um bloqueio no nível de tabela ou no nível de linha fica aguardando indefinidamente pela liberação do bloqueio conflitante. Por isso, é uma péssima idéia as aplicações manterem transações abertas por longos períodos (por exemplo, aguardando a entrada de dados pelo usuário).