MySQL: una transazione bloccherà la riga?


13

Non ho mai provato a utilizzare la transazione MySQL prima, voglio solo chiarire qualcosa.

Se due utenti eseguono una query nel momento esatto, come MySQL lo gestirà? ad esempio, gli utenti stanno provando ad aggiornare un record.

user1: aggiorna la tabella impostata column = column - 4 dove column_id = 1;

user2: aggiorna il set di tabelle column = column - 7 dove column_id = 1;

Ora, se utilizzo le transazioni, MySQL sceglierà quale query verrà eseguita per prima e bloccherà il secondo utente fino a quando la prima query non viene impegnata? Sarà un blocco tabella o un blocco riga?

Cosa succede se un terzo utente emetterà un'istruzione select? Quale sarà il valore restituito da MySQL?

PS questo sarà su Innodb.

Risposte:


17

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, BEGINviene 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".

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.