Postgres, MVCC e bloqueio

8

Eu tenho uma série de instruções SQL que se parecem com o seguinte:

BEGIN;
SELECT counter FROM table WHERE id=X FOR UPDATE;
REALLY COMPLEX QUERY;
UPDATE table SET counter=Y WHERE id=X;
END;

Eu gostaria de impedir que o contador fosse lido enquanto recalculava seu valor, mas de acordo com os documentos do Postgres "Os bloqueios no nível da linha não afetam a consulta de dados; eles bloqueiam apenas gravadores na mesma linha".

Questões:

  1. Qual o sentido de um bloqueio de linha "exclusivo" se não impedir leituras? É apenas para impedir que outras transações recebam bloqueios de compartilhamento?
  2. Se eu ler a linha com SELECT ... FOR SHARE, isso atinge o mesmo efeito que um bloqueio "exclusivo"?
  3. É possível desativar o MVCC para uma tabela / esquema / banco de dados e permitir gravações no local?
Kevin
fonte

Respostas:

5

1) Qualquer outra sessão lerá os dados modificados por sua transação como antes da sua declaração "BEGIN", desde que sua transação não seja confirmada. Assim que sua transação for confirmada, ela lerá o novo valor do contador. O ponto é que outros não precisam esperar e sempre verão uma base de dados consistente.

2), 3) Por que você não tenta com "ACCESS EXCLUSIVE"? (consulte http://www.postgresql.org/docs/current/static/explicit-locking.html )

EDIT: Se você não gosta de bloquear a tabela inteira com um bloqueio "ACCESS EXCLUSIVE", também pode usar um "Bloqueio Consultivo" (consulte a seção 13.3.4 no link acima).

jp
fonte
1

Se as leituras que você deseja bloquear forem execuções simultâneas da mesma transação apenas com dados diferentes, use uma instrução UPDATE com a cláusula return.

Confira os documentos na instrução UPDATE. Para responder sua pergunta sobre o ponto de um bloqueio de linha exclusivo, é para evitar inconsistência de dados de atualizações simultâneas. Os leitores têm uma visão consistente do banco de dados o tempo todo.

Ketema
fonte
1

No exemplo de código que você forneceu, todas as leituras dessa linha verão o valor antigo até a transação ser confirmada.

1) Considere a amostra de código sendo executada duas vezes simultaneamente, com o mesmo valor de X. Se a instância A foi executada select ... for update, ela bloqueou essa linha até ser confirmada. Instância B, fazendo o mesmo, vai bloquear na tentativa de tomar o bloqueio da mesma maneira. Somente quando A confirma, B pode continuar. B estará recebendo o valor A deixado para trás em sua atualização final.

Se Ydepender de qual valor a select ... for updateconsulta lê, ou outra leitura dela na parte 'complexa', isso terá o mesmo efeito de se fossem executadas em série - você não obtém a condição de corrida em que um dos resultados será descartado.

Se Yfor puramente o resultado das consultas complexas no meio, executá-lo em paralelo sem select ... for updateque ambos façam a mesma atualização.

2) for sharepermitirá que outras seleções nessa linha prossigam, mas fará com que qualquer outra coisa tente select ... for updateou update ...bloqueie até terminar. Se o código de exemplo fosse executado duas vezes, simultaneamente, eles entrariam em conflito ao alcançar a updateinstrução, fazendo com que um deles fosse abortado.

3) Não. Fazer isso é perigoso, pois um leitor pode ler um campo meio atualizado. (Ou leia o restante de um campo atualizado após o início da leitura.) Também pode quebrar a consistência, mostrando ao leitor um valor atualizado após o início de sua transação.

MaHuJa
fonte