SELECTy FOR UPDATE
Może się zdarzyć, że będziemy chcieli przeczytać rekord, w celu zmiany wartości niektórych z jego pól, mając jednocześnie pewność, że nikt inny nie będzie chciał w tym samym czasie wykonać tego samego. Na przykład dwóch użytkowników w czasie dwóch różnych sesji czytają ten sam rekord, w celu wstawienia następnego rekordu w którym pewna wartośc w pewnym polu będzie zwiększoną inkrementalnie wartością z pola przeczytanego właśnie rekordu, albo wartością maksymalną w tym polu (bierzącą wartością maksymlaną).
Sesja 1
mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> SELECT MAX(f) FROM tab; +--------+ | MAX(f) | +--------+ | 3 | +--------+ 1 row in set (0.00 sec) mysql> INSERT INTO tab(f) VALUES (4); Query OK, 1 row affected (0.00 sec) |
Sesja 2
mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> SELECT MAX(f) FROM tab; +--------+ | MAX(f) | +--------+ | 3 | +--------+ 1 row in set (0.00 sec) mysql> INSERT INTO tab(f) VALUES (4); Query OK, 1 row affected (0.00 sec) mysql> COMMIT; Query OK, 0 rows affected (0.00 sec) |
Sesja 1
mysql> COMMIT; Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM tab; +------+ | f | +------+ | 1 | | 2 | | 3 | | 4 | | 4 | +------+ 5 rows in set (0.00 sec) |
W wyniku takich działań powstały dwa rekordy z wartością 4, podczas gdy chcieliśmy mieć jeden rekord z wartością 4 i jeden z wartością 5.
Aby zabezpieczyć się przed taką sytuacją musimy ograniczyć dostęp do rekordów tablicy. Można to zrobić za pomocą zamknięcia dostępu do tablicy do czasu, aż transakcja nie zostanie zakończona. Służy do tego klauzula FOR UPDATE dodawana do polecenia SELECT. Jest to więc specjalny SELECT wykonywany z myślą o tym, aby chwilę później wykonać UPDATE.
W przykładzie poniżej najpierw usuwamy błędne rekordy.
mysql> DELETE FROM tab WHERE f=4; Query OK, 2 rows affected (0.00 sec) mysql> SELECT * FROM tab; +------+ | f | +------+ | 1 | | 2 | | 3 | +------+ 3 rows in set (0.00 sec) mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> SELECT MAX(f) FROM tab FOR UPDATE; +--------+ | MAX(f) | +--------+ | 3 | +--------+ 1 row in set (0.00 sec) mysql> INSERT INTO tab(f) VALUES (4); Query OK, 1 row affected (0.00 sec) |
Sesja 2
mysql> SELECT MAX(f) FROM tab FOR UPDATE; |
Nie ma żadnych wyników. MySQL czeka, aż aktywna transakcja się zakończy i dopiero wówczas zwróci dane, które będą aktualne po zakończeniu transakcji w sesji 1.
Sesja 1
mysql> COMMIT; Query OK, 0 rows affected (0.00 sec) |
Dopierow w tym momencie wyniki są zwracane do sesji 2. Należy jeszcze dodać, że jeśli blokowanie trwało zbyt długo, wówczas MySQL zwróci informację, że został przekroczony czas oczekiwania.
Sesja 2
mysql> SELECT MAX(f) FROM tab FOR UPDATE; +--------+ | MAX(f) | +--------+ | 4 | +--------+ 1 row in set (4.20 sec) mysql> INSERT INTO tab(f) VALUES(5); Query OK, 1 row affected (0.00 sec) mysql> COMMIT; Query OK, 0 rows affected (0.01 sec) mysql> SELECT * FROM tab; +------+ | f | +------+ | 1 | | 2 | | 3 | | 4 | | 5 | +------+ 5 rows in set (0.00 sec) |
Źródło: http://gryf.mif.pg.gda.pl/~mate/wyklady/bazy/transakcje.html
Listing
Ranga: Administrator serwisu Punktów: 0