Jak poprawnie wdrożyć optymistyczne blokowanie w MySQL?
Nasz zespół wydedukował, że musimy zrobić nr 4 poniżej, w przeciwnym razie istnieje ryzyko, że inny wątek może zaktualizować tę samą wersję rekordu, ale chcielibyśmy potwierdzić, że jest to najlepszy sposób.
- Utwórz pole wersji w tabeli, w której chcesz użyć blokowania optymistycznego, np. Nazwa kolumny = „wersja”
- W zaznaczeniach pamiętaj o dołączeniu kolumny wersji i zanotuj wersję
- Po kolejnej aktualizacji rekordu instrukcja aktualizacji powinna wydać „gdzie wersja = X” gdzie X jest wersją otrzymaną w # 2 i ustawić pole wersji podczas tej instrukcji aktualizacji na X + 1
- Wykonaj
SELECT FOR UPDATE
rekord, który zamierzamy zaktualizować, aby serializować, kto może wprowadzić zmiany w rekordzie, który próbujemy zaktualizować.
Aby to wyjaśnić, staramy się, aby dwa wątki, które wybierają ten sam rekord w tym samym oknie czasowym, w którym pobierają tę samą wersję rekordu, nie nadpisywały się nawzajem, gdyby próbowały zaktualizować rekord w tym samym czasie. Uważamy, że jeśli nie zrobimy # 4, istnieje szansa, że jeśli oba wątki wejdą w odpowiednie transakcje w tym samym czasie (ale nie wydały jeszcze swoich aktualizacji), to kiedy przejdą do aktualizacji, drugi wątek, który użyje aktualizacji ... gdzie wersja = X będzie działać na starych danych.
Czy słusznie myślimy, że musimy dokonać tego pesymistycznego blokowania podczas aktualizacji, nawet jeśli korzystamy z pól wersji / blokowania optymistycznego?
SELECT ... FOR UPDATE
czy zamek optymistyczne wersji wiersza, a nie obu. Zobacz szczegóły w odpowiedzi.Odpowiedzi:
Twój programista się myli. Potrzebujesz wersji
SELECT ... FOR UPDATE
lub wiersza, a nie obu.Wypróbuj i przekonaj się. Otwarte trzy sesje MySQL
(A)
,(B)
a(C)
do tej samej bazy danych.W
(C)
numerze:W obu
(A)
i(B)
wydaćUPDATE
że testy oraz zestawy wersji wiersza, zmianawinner
tekstu w każdym więc można zobaczyć, który jest sesja, która:Teraz
(C)
,UNLOCK TABLES;
aby zwolnić blokadę.(A)
i(B)
będzie walczył o blokadę rzędów. Jeden z nich wygra i zdobędzie zamek. Drugi zablokuje się na zamku. Zwycięzca, który uzyskał blokadę, przystąpi do zmiany wiersza. Zakładając,(A)
że wygrał, możesz teraz zobaczyć zmieniony wiersz (nadal niezaangażowany, więc niewidoczny dla innych transakcji) za pomocąSELECT * FROM test WHERE id = 1
.COMMIT
Powiedzmy, że teraz w sesji zwycięzcy(A)
.(B)
otrzyma blokadę i rozpocznie aktualizację. Jednak wersja nie jest już zgodna, więc nie zmieni żadnych wierszy, jak wynika z wyniku liczby wierszy. Tylko jedenUPDATE
miał jakikolwiek efekt, a aplikacja kliencka może wyraźnie zobaczyć, które sięUPDATE
powiodły, a które nie. Dalsze blokowanie nie jest konieczne.Zobacz dzienniki sesji na pastebin tutaj . Użyłem
mysql --prompt="A> "
itp., Aby łatwo odróżnić sesje. Skopiowałem i wkleiłem wynik przepleciony w sekwencji czasowej, więc nie jest to całkowicie surowy wynik i możliwe, że popełniłem błędy podczas kopiowania i wklejania. Sprawdź to sam, aby zobaczyć.Gdybyś nie dodał pole wersji rząd, wtedy trzeba by
SELECT ... FOR UPDATE
móc wiarygodnie zapewnić zamówieniu.Jeśli się nad tym zastanowisz, a
SELECT ... FOR UPDATE
jest całkowicie zbędne, jeśli natychmiast robisz toUPDATE
bez ponownego wykorzystywania danych zSELECT
lub jeśli używasz wersji wiersza. IUPDATE
tak zabierze zamek. Jeśli ktoś inny zaktualizuje wiersz między Twoim odczytem a późniejszym zapisem, Twoja wersja nie będzie już zgodna, więc aktualizacja się nie powiedzie. Tak działa optymistyczne blokowanie.Celem
SELECT ... FOR UPDATE
jest:SERIALIZABLE
izolacji lub wersjonowania wiersza.Nie musisz używać zarówno optymistycznego blokowania (wersjonowanie wierszy), jak i
SELECT ... FOR UPDATE
. Użyj jednego lub drugiego.źródło
Brak blokad (bez tabeli, bez transakcji), a nawet pożądanych:
źródło