To, czego chcesz, to WYBIERZ ... DO AKTUALIZACJI w kontekście transakcji. WYBIERZ NA AKTUALIZACJĘ blokuje wyłączne zaznaczenie wybranych wierszy, tak jakbyś wykonywał aktualizację. Działa również domyślnie na poziomie izolacji PRZECZYTAJ ZOBOWIĄZANIE, niezależnie od tego, na jaki poziom izolacji jest jawnie ustawiony. Pamiętaj tylko, że WYBIERZ ... DO AKTUALIZACJI jest bardzo zły dla współbieżności i powinien być używany tylko wtedy, gdy jest to absolutnie konieczne. Ma również tendencję do namnażania się w bazie kodu, gdy ludzie wycinają i wklejają.
Oto przykładowa sesja z bazy danych Sakila, która demonstruje niektóre zachowania zapytań FOR UPDATE.
Po pierwsze, abyśmy byli krystalicznie czysti, ustaw poziom izolacji transakcji na POWTARZALNE CZYTANIE. Jest to zwykle niepotrzebne, ponieważ jest to domyślny poziom izolacji dla InnoDB:
session1> SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
session1> BEGIN;
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | WILLIAMS |
+------------+-----------+
1 row in set (0.00 sec)
W drugiej sesji zaktualizuj ten wiersz. Linda wyszła za mąż i zmieniła imię:
session2> UPDATE customer SET last_name = 'BROWN' WHERE customer_id = 3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Z powrotem w sesji 1, ponieważ byliśmy w REPEATABLE READ, Linda nadal jest LINDA WILLIAMS:
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | WILLIAMS |
+------------+-----------+
1 row in set (0.00 sec)
Ale teraz chcemy wyłącznego dostępu do tego wiersza, dlatego w wierszu wywołujemy FOR UPDATE. Zauważ, że otrzymujemy teraz najnowszą wersję wiersza, która została zaktualizowana w sesji 2 poza tą transakcją. To NIE POWTARZANE CZYTANIE, to CZYTANIE ZOBOWIĄZANE
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3 FOR UPDATE;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | BROWN |
+------------+-----------+
1 row in set (0.00 sec)
Przetestujmy blokadę ustawioną w session1. Pamiętaj, że session2 nie może zaktualizować wiersza.
session2> UPDATE customer SET last_name = 'SMITH' WHERE customer_id = 3;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
Ale nadal możemy z niego wybierać
session2> SELECT c.customer_id, c.first_name, c.last_name, a.address_id, a.address FROM customer c JOIN address a USING (address_id) WHERE c.customer_id = 3;
+-------------+------------+-----------+------------+-------------------+
| customer_id | first_name | last_name | address_id | address |
+-------------+------------+-----------+------------+-------------------+
| 3 | LINDA | BROWN | 7 | 692 Joliet Street |
+-------------+------------+-----------+------------+-------------------+
1 row in set (0.00 sec)
Nadal możemy aktualizować tabelę podrzędną za pomocą relacji klucza obcego
session2> UPDATE address SET address = '5 Main Street' WHERE address_id = 7;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1 Changed: 1 Warnings: 0
session1> COMMIT;
Kolejnym efektem ubocznym jest znaczne zwiększenie prawdopodobieństwa spowodowania impasu.
W twoim konkretnym przypadku prawdopodobnie chcesz:
BEGIN;
SELECT id FROM `items` WHERE `status`='pending' LIMIT 1 FOR UPDATE;
-- do some other stuff
UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `id`=<selected id>;
COMMIT;
Jeśli fragment „zrób coś innego” jest niepotrzebny i nie musisz tak naprawdę przechowywać informacji o wierszu, wybierz WYBIERZ AKTUALIZACJĘ jest niepotrzebny i marnotrawny, a zamiast tego możesz po prostu uruchomić aktualizację:
UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `status`='pending' LIMIT 1;
Mam nadzieję, że to ma jakiś sens.
items
WHEREstatus
=„ w toku ”LIMIT 1 DO AKTUALIZACJI;” i oboje widzą ten sam rząd, a następnie jeden zablokuje drugi. Miałem nadzieję, że jakoś uda się ominąć zamknięty rząd i przejść do następnego, który był w toku.