Una singola istruzione simile funziona allo stesso modo con MyISAM o InnoDB, con una transazione o con autocommit = ON. Si blocca abbastanza per fare la query, bloccando così l'altra connessione. Al termine, l'altra connessione procede. In tutti i casi, la colonna viene presto ridotta di 11.
Un terzo utente può vedere il valore decrementato di 0 o 4 o 7 o 11. Il "tempo esatto" non è realmente possibile perché, ad un certo punto nell'esecuzione di ogni istruzione, viene controllato / impostato un blocco a thread singolo / qualunque cosa . Cioè, saranno serializzati, così in fretta che non puoi vederlo.
InnoDB blocca solo le righe, non le tabelle. (OK, l'istruzione DDL esegue blocchi più audaci.)
Ciò che diventa più interessante è una transazione che modifica due cose o che richiede un notevole lasso di tempo:
Caso di intenzione: oggetto singolo ma che richiede tempo:
BEGIN;
SELECT something;
think about it for a while
UPDATE that something;
COMMIT;
La selezione deve essere scritta in questo modo:
SELECT something FOR UPDATE;
Questo dice ad altre connessioni "Ho intenzione di aggiornare la riga; per favore non incasinarmi". (Cito questo esempio, perché molti neofiti mancano questa sottigliezza.)
Caso deadlock: pasticciare con 2 cose:
BEGIN; -- in one connection
UPDATE thing_1;
UPDATE thing_2;
COMMIT;
BEGIN; -- in another connection, at the "exact same time"
UPDATE thing_2;
UPDATE thing_1;
COMMIT;
Questo è il classico esempio di deadlock: ognuno afferra una cosa e poi raggiunge l'altra cosa. Chiaramente non può essere fatto funzionare. Una transazione viene uccisa; l'altro completa. Quindi, è necessario verificare la presenza di errori, in modo da poterlo scoprire.
La normale reazione a un deadlock è quella di riprodurre l'intera transazione non riuscita. A quel punto, l'altra connessione non interferirà e dovrebbe procedere senza problemi. (OK, un'altra connessione potrebbe creare un altro deadlock.)
Caso di ritardo: se le due connessioni afferrano più cose nello stesso ordine, una può essere ritardata fino al termine dell'altra. Per evitare che ciò sia "in attesa per sempre", esiste un valore predefinito di 50 secondi innodb_lock_wait_timeout
. La tua coppia di semplici UPDATEs
è in realtà un esempio di questo caso. Uno finirà prontamente; l'altro è bloccato fino al termine del primo.
Nota come un deadlock può (in alcuni casi) essere trasformato in un ritardo ordinando costantemente le cose che tocchi.
autocommit = 1: con questa impostazione e senza chiamare BEGIN
, ogni istruzione è effettivamente:
BEGIN;
your statement
COMMIT;
autocommit = 0: questo è un problema in attesa di accadere. Quando si esegue una query di scrittura, BEGIN
viene generato implicitamente un. Tuttavia, è tua responsabilità emettere eventualmente COMMIT
. Se non lo fai, ti chiederai perché il tuo sistema è bloccato. (Un altro bug comune per i principianti.) Il mio consiglio: "Non usare mai =0
".