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 UPDATE
deve 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]
- quartos contém
- 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 SERIALIZABLE
isolamento de transação versus READ_COMMITTED
comSELECT ... 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ê.
REPEATABLE_READ
eREAD_COMMITTED
até opções portáteis? Os únicos resultados que obtenho são para o servidor MSSQLREAD COMMITTED
modo 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.select ... for update
onrooms
ainda permitirároom_tags
a exclusão porque são tabelas separadas. Você quis dizer se afor update
cláusula impedirá exclusões derooms
?Respostas:
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 UPDATE
explicitamente.Isso depende do controle de simultaneidade que seu sistema de banco de dados está usando.
MyISAM
inMySQL
(e em vários outros sistemas antigos) bloqueia a tabela inteira durante uma consulta.Em
SQL Server
, asSELECT
consultas colocam bloqueios compartilhados nos registros / páginas / tabelas que examinaram, enquanto asDML
consultas 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,SELECT
ou aDELETE
consulta será bloqueada até que outra sessão seja confirmada.Nos bancos de dados que uso
MVCC
(comoOracle
,PostgreSQL
,MySQL
comInnoDB
), aDML
consulta 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, umSELECT FOR UPDATE
seria útil: bloquearia umSELECT
ou aDELETE
consulta até que outra sessão fosse confirmada, exatamente como oSQL Server
faz.Geralmente,
REPEATABLE READ
não proíbe linhas fantasmas (linhas que apareceram ou desapareceram em outra transação, em vez de serem modificadas)Em versões
Oracle
anterioresPostgreSQL
,REPEATABLE READ
é na verdade um sinônimo deSERIALIZABLE
. Basicamente, isso significa que a transação não vê alterações feitas depois de iniciada. Portanto, nesta configuração, a últimaThread 1
consulta 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 READ
eSERIALIZABLE
são coisas diferentes: os leitores noSERIALIZABLE
modo definem bloqueios de próxima chave nos registros que avaliam, evitando efetivamente o simultâneoDML
neles. Portanto, você não precisa de umSELECT FOR UPDATE
no modo serializável, mas precisa deles noREPEATABLE READ
ouREAD 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".fonte
SERIALIZABLE
deveria ser usado versusREAD_COMMITTED
comSELECT ... FOR UPDATE
. Você pode atualizar sua resposta para refletir esta pergunta atualizada?SELECT FOR UPDATE
no modo serializável", comInnoDB
. Com os outrosMVCC
sistemas, os dois são sinônimos e você precisaSELECT FOR UPDATE
.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ídaSELECT
até o final da transação. Portanto, a resposta não deveria ser simplesmenteYes
com as referências de apoio abaixo?Respostas curtas:
Q1: Sim.
P2: Não importa qual você usa.
Resposta longa:
A
select ... for update
irá (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:
Como as linhas afetadas por
my_condition
estã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.
fonte
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.