Isolamento serializável - Amazon Redshift

Isolamento serializável

Alguns aplicativos exigem não apenas consultas e o carregamentos simultâneos, mas também a capacidade de gravar em várias tabelas ou na mesma tabela simultaneamente. Nesse contexto, simultaneamente significa em sobreposição, e não programada para execução precisamente ao mesmo tempo. Duas transações são consideradas simultâneas se a segunda começar antes da confirmação da primeira transação. Operações simultâneas podem originar de sessões diferentes que são controladas pelo mesmo usuário ou por usuários diferentes.

nota

O Amazon Redshift oferece suporte a um comportamento de confirmação automática padrão em que cada comando SQL executado separadamente é confirmado individualmente. Se você inserir um conjunto de comandos em um bloco de transação (definido pelas instruções BEGIN e END), o bloco é confirmado como uma transação, de forma que você possa revertê-la se necessário. Exceções a esse comportamento são os comandos TRUNCATE e VACUUM, que confirmam automaticamente todas as alterações pendentes feitas na transação atual.

Alguns clientes SQL emitem comandos BEGIN e COMMIT automaticamente, de modo que o cliente possa controlar se um grupo de instruções é executado como uma transação ou cada instrução individual é executada como sua própria transação. Verifique a documentação da interface que você está usando. Por exemplo, ao usar o driver Amazon Redshift JDBC, um PreparedStatement JDBC com uma string de consulta que contém vários comandos SQL (separados por ponto e vírgula) executa todas as instruções como uma única transação. Por outro lado, se você usar SQL Workbench/J, definir AUTO COMMIT ON e executar várias instruções, cada instrução será executada como sua própria transação.

As operações de gravação simultâneas são aceitas no Amazon Redshift de forma protetora, usando bloqueios de gravação em tabelas e o princípio de isolamento serializável. O isolamento serializável preserva a ilusão de que uma transação em execução em uma tabela é a única transação em execução naquela tabela. Por exemplo, duas transações em execução simultânea, T1 e T2, devem produzir os mesmos resultados que pelo menos uma das seguintes opções:

  • T1 e T2 são executadas em série nessa ordem.

  • T2 e T1 são executadas em série nessa ordem.

As transações simultâneas são invisíveis entre si; não podem detectar as alterações uma das outras. Cada transação simultânea criará um snapshot do banco de dados no início da transação. Um snapshot do banco de dados é criado em uma transação na primeira ocorrência da maioria das instruções SELECT, comandos DML tais como COPY, DELETE, INSERT, UPDATE e TRUNCATE, e após os seguintes comandos DDL:

  • ALTER TABLE (para adicional ou descartar colunas)

  • CRIAR TABELA

  • DESCARTAR TABELA

  • TRUNCATE TABLE

Se qualquer execução serial das transações simultâneas produzir os mesmos resultados que sua execução simultânea, essas transações são consideradas "serializáveis" e podem ser executadas com segurança. Se nenhuma execução serial dessas transações pode produzir os mesmos resultados, a transação que executa uma instrução que pode quebrar a capacidade de serializar é interrompida e revertida.

As tabelas de catálogo de sistema (PG) e outras tabelas de sistema do Amazon Redshift (STL e STV) não são bloqueadas em uma transação. Portanto, alterações nos objetos do banco de dados decorrentes de operações DDL e TRUNCATE são visíveis na confirmação para quaisquer transações simultâneas.

Por exemplo, suponha que a tabela A exista no banco de dados quando duas transações simultâneas, T1 e T2, começam. Suponha que T2 retorna uma lista de tabelas selecionando na tabela de catálogo PG_TABLES. Em seguida, T1 descarta a tabela A e confirmações e, em seguida, T2 lista as tabelas novamente. A tabela A agora não está mais listada. Se T2 tentar consultar a tabela descartada, o Amazon Redshift retornará um erro "relação não existe". A consulta de catálogo que retorna a lista de tabelas para T2 ou verifica se a tabela A existe não está sujeita às mesmas regras de isolamento que as operações realizadas nas tabelas do usuário.

Transações para atualizações dessas tabelas são executadas em um modo de isolamento de leitura confirmada. As tabelas de catálogo com prefixo PG não oferecem suporte ao isolamento de snapshot.

Isolamento serializável para tabelas de sistema e tabelas de catálogo

Um snapshot do banco de dados também é criado em uma transação para qualquer consulta SELECT que faça referência a uma tabela criada pelo usuário ou tabela de sistema Amazon Redshift (STL ou STV). Consultas SELECT que não fazem referência a nenhuma tabela não criam um novo snapshot de banco de dados de transação. Instruções INSERT, DELETE e UPDATE que operam exclusivamente em tabelas de catálogo do sistema (PG) também não criam um snapshot de banco de dados da nova transação.

Como corrigir erros de isolamento serializável

ERROR:1023 DETAIL: Violação de isolamento serializável em uma tabela no Redshift

Quando o Amazon Redshift detecta um erro de isolamento serializável, você vê uma mensagem de erro como a seguir.

ERROR:1023 DETAIL: Serializable isolation violation on table in Redshift

Para resolver um erro de isolamento serializável, você pode tentar um dos seguintes métodos:

  • Tente executar a transação cancelada novamente.

    O Amazon Redshift detectou que um workload simultâneo não é serializável. Ele sugere lacunas na lógica da aplicação, que geralmente podem ser contornadas repetindo a transação que encontrou o erro. Se o problema persistir, tente um dos outros métodos.

  • Mova todas as operações que não precisam estar na mesma transação atômica para fora da transação.

    Este método é aplicável quando operações individuais em duas transações diferentes fazem referências cruzadas de maneira que possa afetar os resultados. Por exemplo, as duas sessões a seguir iniciam uma transação.

    Session1_Redshift=# begin;
    Session2_Redshift=# begin;

    O resultado de uma instrução SELECT em uma transação pode ser afetado por uma instrução INSERT na outra. Ou seja, suponhamos que você esteja executando as seguintes instruções em série, em qualquer ordem. Em todo caso, o resultado é uma das instruções SELECT retornar uma linha a mais do que ocorreria caso as transações fossem executadas simultaneamente. Não há ordem na qual as operações possam ser executadas em série e ainda obter o mesmo resultado da execução simultânea. Portanto, a última operação executada resulta em um erro de isolamento serializável.

    Session1_Redshift=# select * from tab1; Session1_Redshift=# insert into tab2 values (1);
    Session2_Redshift=# insert into tab1 values (1); Session2_Redshift=# select * from tab2;

    Muitas vezes, o resultado das instruções SELECT não é relevante. Ou seja, a atomicidade das operações nas transações não é importante. Nesses casos, mova as instruções SELECT para fora das transações, conforme mostrado nos exemplos a seguir.

    Session1_Redshift=# begin; Session1_Redshift=# insert into tab1 values (1) Session1_Redshift=# end; Session1_Redshift=# select * from tab2;
    Session2_Redshift # select * from tab1; Session2_Redshift=# begin; Session2_Redshift=# insert into tab2 values (1) Session2_Redshift=# end;

    Nesses exemplos, não há referências cruzadas nas transações. As duas instruções INSERT não afetam uma à outra. Nesses exemplos, há pelo menos uma ordem na qual as transações podem ser executadas em série e obter o mesmo resultado da execução simultânea. Isso significa que as transações são serializáveis.

  • Bloqueie todas as tabelas nas sessões para forçar a serialização.

    O comando LOCK bloqueia as operações que podem resultar em erros de isolamento serializado. Ao usar o comando LOCK, faça o seguinte:

    • Bloqueie todas as tabelas afetadas pela transação, incluindo aquelas afetadas por instruções SELECT somente leitura na transação.

    • Bloqueie as tabelas na mesma ordem, independentemente da ordem na qual as operações são executadas.

    • Bloqueie todas as tabelas no início da transação, antes de executar qualquer operação.

  • Use o isolamento de snapshot para transações simultâneas

    Use um comando ALTER DATABASE com isolamento de snapshot. Para obter mais informações sobre o parâmetro SNAPSHOT para ALTER DATABASE, consulte Parâmetros.

ERROR:1018 DETAIL: A relação não existe

Ao executar operações simultâneas do Amazon Redshift em sessões diferentes, uma mensagem de erro semelhante a seguinte é exibida.

ERROR: 1018 DETAIL: Relation does not exist.

As transações no Amazon Redshift seguem o isolamento de snapshot. Após o início de uma transação, o Amazon Redshift obtém um snapshot do banco de dados. Para todo o ciclo de vida da transação, a transação opera no estado do banco de dados conforme refletido no snapshot. Se a transação lê de uma tabela que não existe no snapshot, ela lança a mensagem de erro 1018 mostrada anteriormente. Mesmo quando outra transação concorrente cria uma tabela após a transação ter obtido o snapshot, a transação não pode ler a partir da tabela recém-criada.

Para resolver este erro de isolamento de serialização, você pode tentar mover o início da transação para um ponto onde você sabe que a tabela existe.

Se a tabela é criada por outra transação, esse ponto é pelo menos depois que essa transação foi confirmada. Além disso, certifique-se de que nenhuma transação simultânea foi confirmada que possa ter descartado a tabela.

session1 = # BEGIN; session1 = # DROP TABLE A; session1 = # COMMIT;
session2 = # BEGIN;
session3 = # BEGIN; session3 = # CREATE TABLE A (id INT); session3 = # COMMIT;
session2 = # SELECT * FROM A;

A última operação executada como operação de leitura pela sessão2 resulta em um erro de isolamento serializável. Esse erro ocorre quando session2 tira um snapshot e a tabela já foi descartada por uma session1 confirmada. Em outras palavras, mesmo que uma sessão3 simultânea tenha criado a tabela, session2 não vê a tabela porque ela não está no snapshot.

Para resolver esse erro, é possível reordenar as sessões da forma a seguir.

session1 = # BEGIN; session1 = # DROP TABLE A; session1 = # COMMIT;
session3 = # BEGIN; session3 = # CREATE TABLE A (id INT); session3 = # COMMIT;
session2 = # BEGIN; session2 = # SELECT * FROM A;

Agora, quando session2 tira seu snapshot, session3 já foi confirmada e a tabela está no banco de dados. Session2 pode ler a partir da tabela sem qualquer erro.