Quando usar SELECT… FOR UPDATE?

119

Por favor me ajude a entender o caso de uso por trás SELECT ... FOR UPDATE.

Pergunta 1 : O seguinte é um bom exemplo de quando SELECT ... FOR UPDATEdeve ser usado?

Dado:

  • quartos [id]
  • tags [id, nome]
  • room_tags [room_id, tag_id]
    • room_id e tag_id são chaves estrangeiras

O aplicativo deseja listar todas as salas e suas marcas, mas precisa diferenciar as salas sem marcas das salas que foram removidas. Se SELECT ... FOR UPDATE não for usado, o que pode acontecer é:

  • Inicialmente:
    • quartos contém [id = 1]
    • tags contém [id = 1, name = 'cats']
    • room_tags contém [room_id = 1, tag_id = 1]
  • Tópico 1: SELECT id FROM rooms;
    • returns [id = 1]
  • Tópico 2: DELETE FROM room_tags WHERE room_id = 1;
  • Tópico 2: DELETE FROM rooms WHERE id = 1;
  • Tópico 2: [confirma a transação]
  • Tópico 1: SELECT tags.name FROM room_tags, tags WHERE room_tags.tag_id = 1 AND tags.id = room_tags.tag_id;
    • retorna uma lista vazia

Agora, o Tópico 1 pensa que a sala 1 não tem etiquetas, mas na realidade a sala foi removida. Para resolver este problema, o Tópico 1 deve SELECT id FROM rooms FOR UPDATE, evitando assim que o Tópico 2 exclua derooms até que o Thread 1 seja concluído. Isso está correto?

Pergunta 2 : Quando se deve usar o SERIALIZABLEisolamento de transação versus READ_COMMITTEDcomSELECT ... FOR UPDATE ?

Espera-se que as respostas sejam portáteis (não específicas do banco de dados). Se isso não for possível, explique o porquê.

Gili
fonte
2
Qual RDBMS você está usando?
Quassnoi
2
@Quassnoi, conforme mencionado no final da pergunta, estou procurando uma solução portátil (não específica para banco de dados).
Gili
2
As opções REPEATABLE_READe READ_COMMITTEDaté opções portáteis? Os únicos resultados que obtenho são para o servidor MSSQL
Billy ONeal
3
@BillyONeal: observe que os modos de isolamento garantem que você não veja peculiaridades que eles não permitem, mas não diga nada sobre as peculiaridades que eles permitem. Isso significa que definir, digamos, o READ COMMITTEDmodo não define se você verá ou não registros confirmados por outra transação: isso apenas garante que você nunca verá registros não confirmados.
Quassnoi
3
A select ... for updateon roomsainda permitirá room_tagsa exclusão porque são tabelas separadas. Você quis dizer se a for updatecláusula impedirá exclusões de rooms?
Chris Saxon

Respostas:

84

A única maneira portátil de obter consistência entre quartos e etiquetas e garantir que os quartos nunca sejam devolvidos após terem sido excluídos é bloqueá-los com SELECT FOR UPDATE.

No entanto, em alguns sistemas, o bloqueio é um efeito colateral do controle de simultaneidade e você obtém os mesmos resultados sem especificar FOR UPDATEexplicitamente.


Para resolver este problema, o Thread 1 deve SELECT id FROM rooms FOR UPDATE, evitando assim que o Thread 2 seja excluído roomsaté que o Thread 1 seja concluído. Isso está correto?

Isso depende do controle de simultaneidade que seu sistema de banco de dados está usando.

  • MyISAMin MySQL(e em vários outros sistemas antigos) bloqueia a tabela inteira durante uma consulta.

  • Em SQL Server, as SELECTconsultas colocam bloqueios compartilhados nos registros / páginas / tabelas que examinaram, enquanto as DMLconsultas colocam bloqueios de atualização (que mais tarde são promovidos a exclusivos ou rebaixados para bloqueios compartilhados). Os bloqueios exclusivos são incompatíveis com os bloqueios compartilhados, portanto, SELECTou a DELETEconsulta será bloqueada até que outra sessão seja confirmada.

  • Nos bancos de dados que uso MVCC(como Oracle, PostgreSQL, MySQLcom InnoDB), a DMLconsulta cria uma cópia do registro (em uma ou outra maneira) e, geralmente, os leitores não bloqueiam escritores e vice-versa. Para esses bancos de dados, um SELECT FOR UPDATEseria útil: bloquearia um SELECTou a DELETEconsulta até que outra sessão fosse confirmada, exatamente como o SQL Serverfaz.

Quando se deve usar o REPEATABLE_READisolamento de transação versus READ_COMMITTEDcom SELECT ... FOR UPDATE?

Geralmente, REPEATABLE READnão proíbe linhas fantasmas (linhas que apareceram ou desapareceram em outra transação, em vez de serem modificadas)

  • Em versões Oracleanteriores PostgreSQL, REPEATABLE READé na verdade um sinônimo de SERIALIZABLE. Basicamente, isso significa que a transação não vê alterações feitas depois de iniciada. Portanto, nesta configuração, a última Thread 1consulta retornará a sala como se ela nunca tivesse sido excluída (o que pode ou não ser o que você queria). Se você não quiser mostrar as salas após terem sido excluídas, você deve bloquear as linhas comSELECT FOR UPDATE

  • Em InnoDB, REPEATABLE READe SERIALIZABLEsão coisas diferentes: os leitores no SERIALIZABLEmodo definem bloqueios de próxima chave nos registros que avaliam, evitando efetivamente o simultâneo DMLneles. Portanto, você não precisa de um SELECT FOR UPDATEno modo serializável, mas precisa deles no REPEATABLE READou READ COMMITED.

Observe que o padrão nos modos de isolamento prescreve que você não vê certas peculiaridades em suas consultas, mas não define como (com bloqueio ou com MVCC ou de outra forma).

Quando digo "você não precisa SELECT FOR UPDATE", realmente deveria ter adicionado "por causa dos efeitos colaterais de certas implementações de mecanismo de banco de dados".

Quassnoi
fonte
1
O último ponto é o cerne da questão, eu acho: "você não precisa de um SELECT FOR UPDATE no modo serializável, mas precisa deles em READ REPEATABLE ou READ COMMITED".
Colin 't Hart
Você está certo. A segunda pergunta deveria ter perguntado quando SERIALIZABLEdeveria ser usado versus READ_COMMITTEDcom SELECT ... FOR UPDATE. Você pode atualizar sua resposta para refletir esta pergunta atualizada?
Gili
1
@Gili: "você não precisa de um SELECT FOR UPDATEno modo serializável", com InnoDB. Com os outros MVCCsistemas, os dois são sinônimos e você precisa SELECT FOR UPDATE.
Quassnoi
1
Acho que a postagem de Colin responde minhas perguntas específicas melhor do que sua resposta, mas agradeço todas as referências que você forneceu. Aceitarei a resposta que melhor combina as duas (respostas específicas no topo, referências de apoio abaixo).
Gili
This depends on the concurrency control your database system is using: Eu acho que você está perdendo os cabelos. Todos os casos listados abaixo indicam que a sala não foi excluída SELECTaté o final da transação. Portanto, a resposta não deveria ser simplesmente Yescom as referências de apoio abaixo?
Gili
33

Respostas curtas:

Q1: Sim.

P2: Não importa qual você usa.

Resposta longa:

A select ... for updateirá (como implica) selecionar certas linhas, mas também bloqueá-las como se já tivessem sido atualizadas pela transação atual (ou como se a atualização de identidade tivesse sido realizada). Isso permite que você os atualize novamente na transação atual e, em seguida, efetue o commit, sem que outra transação seja capaz de modificar essas linhas de qualquer forma.

Outra forma de ver isso é como se as duas instruções a seguir fossem executadas atomicamente:

select * from my_table where my_condition;

update my_table set my_column = my_column where my_condition;

Como as linhas afetadas por my_conditionestão bloqueadas, nenhuma outra transação pode modificá-las de forma alguma e, portanto, o nível de isolamento da transação não faz diferença aqui.

Observe também que o nível de isolamento da transação é independente do bloqueio: definir um nível de isolamento diferente não permite que você evite bloquear e atualizar linhas em uma transação diferente que está bloqueada pela sua transação.

O que os níveis de isolamento da transação garantem (em níveis diferentes) é a consistência dos dados enquanto as transações estão em andamento.

Colin 't Hart
fonte
1
Acho que What transaction isolation levels do guarantee [...] is the consistency of data once transactions are completed.implica incorretamente que os níveis de isolamento não afetam o que acontece durante uma transação. Recomendo revisar esta seção e fornecer mais detalhes sobre como eles afetam o que você vê (ou não vê) durante uma transação.
Gili
1
Acho que sua postagem responde às minhas perguntas específicas melhor do que a de Quassnoi, mas agradeço todas as referências que ele forneceu. Aceitarei a resposta que melhor combina as duas (respostas específicas no topo, referências de apoio abaixo).
Gili
O bloqueio e o isolamento são complicados de forma intercambiável. Então, existem livros para obter o conhecimento sobre isso?
Chao