Quando utilizzare SELEZIONA ... PER AGGIORNAMENTO?


119

Per favore aiutami a capire il caso d'uso dietro SELECT ... FOR UPDATE.

Domanda 1 : il seguente è un buon esempio di quando SELECT ... FOR UPDATEdovrebbe essere usato?

Dato:

  • camere [id]
  • tag [id, nome]
  • room_tags [room_id, tag_id]
    • room_id e tag_id sono chiavi esterne

L'applicazione desidera elencare tutte le stanze virtuali e i relativi tag, ma deve distinguere tra stanze prive di tag e stanze che sono state rimosse. Se SELEZIONA ... PER AGGIORNAMENTO non viene utilizzato, ciò che potrebbe accadere è:

  • inizialmente:
    • stanze contiene [id = 1]
    • tag contiene [id = 1, name = 'cats']
    • room_tags contiene [room_id = 1, tag_id = 1]
  • Discussione 1: SELECT id FROM rooms;
    • returns [id = 1]
  • Discussione 2: DELETE FROM room_tags WHERE room_id = 1;
  • Discussione 2: DELETE FROM rooms WHERE id = 1;
  • Thread 2: [esegue il commit della transazione]
  • Discussione 1: SELECT tags.name FROM room_tags, tags WHERE room_tags.tag_id = 1 AND tags.id = room_tags.tag_id;
    • restituisce un elenco vuoto

Ora il thread 1 pensa che la stanza 1 non abbia tag, ma in realtà la stanza è stata rimossa. Per risolvere questo problema, il thread 1 dovrebbe SELECT id FROM rooms FOR UPDATE, impedendo così l'eliminazione del thread 2 roomsfino al termine del thread 1. È corretto?

Domanda 2 : quando si dovrebbe utilizzare SERIALIZABLEl'isolamento delle transazioni rispetto a READ_COMMITTEDcon SELECT ... FOR UPDATE?

Le risposte dovrebbero essere portabili (non specifiche del database). Se non è possibile, spiega perché.


2
Quale RDBMS stai usando?
Quassnoi

2
@Quassnoi, come accennato in fondo alla domanda, sto cercando una soluzione portatile (non specifica per database).
Gili

2
Sono le opzioni REPEATABLE_READe READ_COMMITTEDanche le opzioni portatili? Gli unici risultati che ottengo per quelli sono per il server MSSQL
Billy ONeal

3
@BillyONeal: nota che le modalità di isolamento garantiscono che non vedi stranezze che non consentono, ma non dire nulla sulle stranezze che consentono. Ciò significa che l'impostazione, ad esempio, READ COMMITTEDmodalità non definisce se si vedranno effettivamente o meno i record salvati da un'altra transazione: si assicura solo che non si vedranno mai i record non salvati.
Quassnoi

3
A select ... for updateon roomsconsentirà comunque room_tagsdi essere cancellato perché sono tabelle separate. Volevi chiedere se la for updateclausola impedirà le cancellazioni da rooms?
Chris Saxon

Risposte:


84

L'unico modo portatile per ottenere coerenza tra stanze e tag e assicurarsi che le stanze non vengano mai restituite dopo che sono state eliminate è bloccarle con SELECT FOR UPDATE.

Tuttavia in alcuni sistemi il blocco è un effetto collaterale del controllo della concorrenza e si ottengono gli stessi risultati senza specificarlo FOR UPDATEesplicitamente.


Per risolvere questo problema, il thread 1 dovrebbe SELECT id FROM rooms FOR UPDATE, impedendo così l'eliminazione del thread 2 roomsfino al termine del thread 1. È corretto?

Ciò dipende dal controllo della concorrenza utilizzato dal sistema di database.

  • MyISAMin MySQL(e molti altri vecchi sistemi) blocca l'intera tabella per la durata di una query.

  • In SQL Server, le SELECTquery inseriscono blocchi condivisi sui record / pagine / tabelle che hanno esaminato, mentre le DMLquery inseriscono blocchi di aggiornamento (che in seguito vengono promossi a blocchi esclusivi o retrocessi a blocchi condivisi). I blocchi esclusivi non sono compatibili con i blocchi condivisi, quindi SELECTo la DELETEquery verrà bloccata finché non viene eseguito il commit di un'altra sessione.

  • Nel database che uso MVCC(come Oracle, PostgreSQL, MySQLcon InnoDB), una DMLquery crea una copia del record (in un modo o nell'altro) e in generale i lettori non bloccare scrittori e viceversa. Per questi database, SELECT FOR UPDATEsarebbe utile: bloccherebbe o SELECTo la DELETEquery fino a quando non viene eseguito il commit di un'altra sessione, proprio come SQL Serverfa.

Quando si dovrebbe utilizzare REPEATABLE_READl'isolamento delle transazioni rispetto a READ_COMMITTEDcon SELECT ... FOR UPDATE?

In generale, REPEATABLE READnon vieta le righe fantasma (righe che sono apparse o scomparse in un'altra transazione, invece di essere modificate)

  • Nelle versioni Oracleprecedenti e precedenti PostgreSQL, REPEATABLE READè in realtà un sinonimo di SERIALIZABLE. Fondamentalmente, questo significa che la transazione non vede le modifiche apportate dopo che è stata avviata. Quindi, in questa configurazione, l'ultima Thread 1query restituirà la stanza come se non fosse mai stata eliminata (il che potrebbe essere o meno quello che volevi). Se non desideri mostrare le stanze virtuali dopo che sono state eliminate, devi bloccare le righe conSELECT FOR UPDATE

  • In InnoDB, REPEATABLE READe SERIALIZABLEsono cose diverse: i lettori in SERIALIZABLEmode impostano i blocchi della chiave successiva sui record che valutano, prevenendo efficacemente il concorrente DMLsu di essi. Quindi non hai bisogno di una SELECT FOR UPDATEmodalità serializzabile, ma ne hai bisogno in REPEATABLE READo READ COMMITED.

Si noti che lo standard sulle modalità di isolamento prescrive che non si vedano certe stranezze nelle query ma non definisce come (con blocco o con MVCCo altro).

Quando dico "non è necessario SELECT FOR UPDATE" avrei dovuto aggiungere "a causa degli effetti collaterali di alcune implementazioni del motore di database".


1
L'ultimo punto è il nocciolo della questione, penso: "non hai bisogno di SELECT FOR UPDATE in modalità serializzabile, ma ne hai bisogno in REPEATABLE READ o READ COMMITED".
Colin 't Hart

Hai ragione. La seconda domanda avrebbe dovuto chiedere quando SERIALIZABLEdovrebbe essere usato rispetto READ_COMMITTEDa SELECT ... FOR UPDATE. Puoi aggiornare la tua risposta per riflettere questa domanda aggiornata?
Gili

1
@ Gili: "non hai bisogno di una SELECT FOR UPDATEmodalità serializzabile", con InnoDB. Con gli altri MVCCsistemi, i due sono sinonimi e ne hai bisogno SELECT FOR UPDATE.
Quassnoi

1
Trovo che il post di Colin risponda alle mie domande specifiche meglio della tua risposta, ma apprezzo tutti i riferimenti che hai fornito. Accetterò una risposta che combini al meglio le due (risposte specifiche in alto, riferimenti di supporto di seguito).
Gili

This depends on the concurrency control your database system is using: Penso che tu stia spaccando i capelli. Tutti i casi che elenchi di seguito indicano che la stanza non viene eliminata tra SELECTla fine della transazione. Quindi, la risposta non dovrebbe essere semplicemente Yescon i riferimenti di supporto di seguito?
Gili

33

Risposte brevi:

Q1: sì.

Q2: Non importa quale usi.

Risposta lunga:

A selezionerà select ... for update(come implica) determinate righe ma le bloccherà anche come se fossero già state aggiornate dalla transazione corrente (o come se l'aggiornamento dell'identità fosse stato eseguito). Ciò consente di aggiornarli nuovamente nella transazione corrente e quindi eseguire il commit, senza che un'altra transazione sia in grado di modificare queste righe in alcun modo.

Un altro modo di vederlo, è come se le seguenti due istruzioni fossero eseguite atomicamente:

select * from my_table where my_condition;

update my_table set my_column = my_column where my_condition;

Poiché le righe interessate da my_conditionsono bloccate, nessun'altra transazione può modificarle in alcun modo e, quindi, il livello di isolamento della transazione non fa differenza qui.

Si noti inoltre che il livello di isolamento della transazione è indipendente dal blocco: l'impostazione di un livello di isolamento diverso non consente di aggirare il blocco e aggiornare le righe in una transazione diversa che sono bloccate dalla transazione.

Ciò che i livelli di isolamento delle transazioni garantiscono (a diversi livelli) è la coerenza dei dati mentre le transazioni sono in corso.


1
Penso che What transaction isolation levels do guarantee [...] is the consistency of data once transactions are completed.erroneamente implichi che i livelli di isolamento non influenzano ciò che accade durante una transazione. Consiglio di rivedere questa sezione e fornire maggiori dettagli su come influiscono su ciò che vedi (o non vedi) durante una transazione.
Gili

1
Trovo che il tuo post risponda alle mie domande specifiche meglio di Quassnoi ma apprezzo tutti i riferimenti che ha fornito. Accetterò una risposta che combini al meglio le due (risposte specifiche in alto, riferimenti di supporto di seguito).
Gili

Il blocco e l'isolamento sono complicati in modo intercambiabile. Quindi ci sono libri per ottenere la conoscenza di questo?
Chao
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.