** Edytowane **
Wybieranie z tabeli docelowej
Od 13.2.9.8. Podzapytania w klauzuli FROM:
Podzapytania w klauzuli FROM mogą zwracać skalar, kolumnę, wiersz lub tabelę. Podzapytania w klauzuli FROM nie mogą być skorelowanymi podzapytaniami, chyba że są używane w klauzuli ON operacji JOIN.
Więc tak, możesz wykonać powyższe zapytanie.
Problem
Tak naprawdę są tutaj dwa problemy. Jest współbieżność, czyli upewnienie się, że nikt inny nie zmienia danych spod naszych stóp. Jest to obsługiwane z blokowaniem. Radzenie sobie z rzeczywistą modyfikacją nowych i starych wartości jest obsługiwane za pomocą tabel pochodnych.
Blokowanie
W przypadku powyższego zapytania, z InnoDB, MySQL najpierw wykonuje SELECT i uzyskuje blokadę odczytu (współdzieloną) dla każdego wiersza w tabeli z osobna. Gdybyś miał klauzulę WHERE w instrukcji SELECT, wtedy tylko wybrane rekordy byłyby zablokowane, a zakresy powodowałyby również zablokowanie wszelkich luk.
Blokada odczytu uniemożliwia innym zapytaniom uzyskanie blokad zapisu, więc rekordy nie mogą być aktualizowane z innego miejsca, gdy są zablokowane do odczytu.
Następnie MySQL uzyskuje blokadę zapisu (na wyłączność) indywidualnie dla każdego rekordu w tabeli. Gdybyś miał klauzulę WHERE w instrukcji UPDATE, to tylko określone rekordy byłyby zablokowane przed zapisem, i ponownie, jeśli klauzula WHERE wybierała zakres, miałbyś zablokowany zakres.
Każdy rekord, który miał blokadę odczytu z poprzedniego SELECT, zostałby automatycznie eskalowany do blokady zapisu.
Blokada zapisu uniemożliwia innym zapytaniom uzyskanie blokady odczytu lub zapisu.
Możesz użyć Innotop, aby to zobaczyć, uruchamiając go w trybie blokady, rozpoczynając transakcję, wykonując zapytanie (ale nie zatwierdzaj go), a zobaczysz blokady w Innotop. Możesz także wyświetlić szczegóły bez Innotop za pomocą SHOW ENGINE INNODB STATUS
.
Zakleszczenia
Twoje zapytanie może ulec zakleszczeniu, jeśli dwie instancje zostały uruchomione w tym samym czasie. Jeśli zapytanie A otrzymało blokady odczytu, a następnie zapytanie B otrzymało blokady odczytu, zapytanie A musiałoby czekać na zwolnienie blokad odczytu zapytania B, zanim mogłoby uzyskać blokady zapisu. Jednak zapytanie B nie zwolni blokad odczytu, dopóki nie zakończy się, i nie zakończy się, dopóki nie uzyska blokad zapisu. Zapytanie A i zapytanie B są w sytuacji patowej, a co za tym idzie, impasu.
Dlatego możesz chcieć wykonać jawną blokadę tabeli, zarówno w celu uniknięcia ogromnej liczby blokad rekordów (która wykorzystuje pamięć i wpływa na wydajność), jak i w celu uniknięcia zakleszczenia.
Alternatywnym podejściem jest użycie SELECT ... FOR UPDATE na wewnętrznym SELECT. To zaczyna się od blokad zapisu we wszystkich wierszach, zamiast zaczynać od odczytu i ich eskalacji.
Tabele pochodne
Dla wewnętrznego SELECT MySQL tworzy pochodną tabelę tymczasową. Tabela pochodna to rzeczywista, nieindeksowana kopia danych, które znajdują się w tabeli tymczasowej, która jest automatycznie tworzona przez MySQL (w przeciwieństwie do tabeli tymczasowej, którą tworzysz jawnie i do której możesz dodawać indeksy).
Ponieważ MySQL używa tabeli pochodnej, jest to tymczasowa stara wartość, do której odwołujesz się w swoim pytaniu. Innymi słowy, nie ma tu magii. MySQL robi to tak, jak robisz to gdziekolwiek indziej, z wartością tymczasową.
Możesz zobaczyć pochodną tabelę, wykonując EXPLAIN w instrukcji UPDATE (obsługiwane w MySQL 5.6+).