PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Nível de isolamento SERIALIZÁVEL no Spring-JDBC


TL;DR:a detecção de conflitos de serialização melhorou drasticamente na página 9.1, então atualize.

É complicado descobrir a partir de sua descrição qual é o SQL real e por que você espera obter uma reversão. Parece que você entendeu mal o isolamento serializável, talvez pensando que ele testa perfeitamente todos os predicados, o que não acontece, especialmente na Pg 8.4.

SERIALIZABLE não garante perfeitamente que as transações sejam executadas como se fossem executadas em série - pois isso seria proibitivamente caro do ponto de vista do desempenho, se fosse possível. Ele fornece apenas verificação limitada. Exatamente o que é verificado e como varia de banco de dados para banco de dados e versão para versão, então você precisa ler os documentos para sua versão do seu banco de dados.

Anomalias são possíveis, onde duas transações são executadas em SERIALIZABLE modo produz um resultado diferente se essas transações forem realmente executadas em série.

Leia a documentação sobre isolamento de transações na Pg para saber mais. Observe que SERIALIZABLE mudou drasticamente o comportamento na Pg 9.1, então certifique-se de ler a versão do manual apropriada para sua versão Pg. Aqui está a versão 8.4 . Em particular, leia 13.2.2.1. Isolamento serializável versus serialização real . Agora compare isso com o suporte de serialização baseado em bloqueio de predicado muito aprimorado descrito no Documentos da página 9.1 .

Parece que você está tentando executar a lógica algo como este pseudocódigo:
count = query("SELECT count(*) FROM the_table");
if (count < threshold):
    query("INSERT INTO the_table (...) VALUES (...)");

Nesse caso, isso não funcionará na página 8.4 quando executado simultaneamente - é praticamente o mesmo que o exemplo de anomalia usado na documentação vinculada acima. Surpreendentemente, ele realmente funciona na página 9.1; Eu não esperava nem mesmo o bloqueio de predicado do 9.1 para capturar o uso de agregados.

Você escreve isso:

mas o 8.4 não detectará que as duas transações são interdependentes, algo que você pode provar trivialmente usando dois psql sessões para testá-lo. É apenas com o material de serialização verdadeiro introduzido no 9.1 que isso funcionará - e, francamente, fiquei surpreso que funcione no 9.1.

Se você quiser fazer algo como impor uma contagem máxima de linhas na página 8.4, você precisa LOCK a mesa para evitar INSERT concorrentes s, fazendo o bloqueio manualmente ou por meio de uma função de gatilho . Fazê-lo em um acionador exigirá inerentemente uma promoção de bloqueio e, portanto, frequentemente um impasse, mas fará o trabalho com sucesso. É melhor feito no aplicativo onde você pode emitir o LOCK TABLE my_table IN EXCLUSIVE MODE antes de obter até mesmo SELECT ing da tabela, então ele já tem o modo de bloqueio mais alto necessário na mesa e, portanto, não deve precisar de promoção de bloqueio propenso a deadlock. O EXCLUSIVE o modo de bloqueio é apropriado porque permite SELECT s, mas nada mais.

Veja como testá-lo em duas sessões do psql:
SESSION 1                               SESSION 2

create table ser_test( x text );

BEGIN TRANSACTION 
ISOLATION LEVEL SERIALIZABLE;


                                        BEGIN TRANSACTION 
                                        ISOLATION LEVEL SERIALIZABLE;

SELECT count(*) FROM ser_test ;

                                        SELECT count(*) FROM ser_test ;

INSERT INTO ser_test(x) VALUES ('bob');


                                        INSERT INTO ser_test(x) VALUES ('bob');

 COMMIT;

                                        COMMIT;

Quando executado em Pg 9.1, o st commits succeeds then the second COMMIT` falha com:
regress=# COMMIT;
ERROR:  could not serialize access due to read/write dependencies among transactions
DETAIL:  Reason code: Canceled on identification as a pivot, during commit attempt.
HINT:  The transaction might succeed if retried.

mas quando executado no 8.4, ambos os commits são bem-sucedidos, porque o 8.4 não tinha todo o código de bloqueio de predicado para serialização adicionado no 9.1.