Rozważ następujący schemat:(Pozostały pozostałe instrukcje dla Twojej wygody) :
-- drop table if exists spies;
create table spies
( id int primary key,
weapon_id int not null,
name varchar(100) not null,
key(weapon_id),
foreign key (weapon_id) references weapons(id)
)engine=InnoDB;
-- drop table if exists weapons;
create table weapons
( id int primary key,
name varchar(100) not null
)engine=InnoDB;
insert weapons(id,name) values (1,'slingshot'),(2,'Ruger');
insert spies(id,weapon_id,name) values (1,2,'Sally');
-- truncate table spies;
Teraz mamy 2 procesy, P1 i P2. Najlepiej przetestować, gdzie P1 to być może MySQL Workbench, a P2 to okno wiersza poleceń MySql. Innymi słowy, musisz ustawić to jako osobne połączenia i dobrze. Musiałbyś mieć skrupulatne oko, aby krok po kroku przeprowadzać je we właściwy sposób (opisany w Narracji poniżej) i zobacz jego wpływ na inne okno procesu.
Rozważ następujące zapytania, pamiętając, że zapytanie mysql, które nie jest opakowane w jawną transakcję, samo w sobie jest transakcją niejawną. Ale poniżej zwróciłem się na wyraźne:
P1:
START TRANSACTION;
-- place1
UPDATE spies SET name = 'Bond', weapon_id = 1 WHERE id = 1;
-- place2
COMMIT;
P2:
START TRANSACTION;
-- place1
UPDATE spies SET name = 'Bond' WHERE id = 1;
-- place2
COMMIT;
P3:
START TRANSACTION;
-- place1
SELECT id into @mine_to_use from weapons where id=1 FOR UPDATE; -- place2
-- place3
COMMIT;
P4:
START TRANSACTION;
-- place1
SELECT id into @mine_to_use from spies where id=1 FOR UPDATE; -- place2
-- place3
COMMIT;
Q5 (mieszanka pytań):
SELECT * from weapons;
SELECT * from spies;
Narracja
P1: Kiedy zaczyna się P1 Q1 , i dostaje się na miejsce2, uzyskał wyłączną blokadę aktualizacji na poziomie rzędu w obu tabelach broni i szpiegów dla rzędu id=1 (łącznie 2 rzędy, 1 rząd w każdym stole). Można to udowodnić, gdy P2 zaczyna biegać w Q3, dociera do miejsca 1, ale blokuje się na miejscu 2 i zostaje zwolniony dopiero, gdy P1 zbliża się do wywołania COMMIT. Wszystko, co właśnie powiedziałem o P2 uruchamiającym Q3, dotyczy tego samego w przypadku P2 uruchamiającego Q4. Podsumowując, na ekranie P2 miejsce 2 zawiesza się do momentu zatwierdzenia P1.
Jeszcze raz uwaga o transakcjach niejawnych. Twoje prawdziwe Zapytanie Q1 wykona to bardzo szybko, a wyjście z niego spowoduje niejawne zatwierdzenie. Jednak poprzedni akapit wyjaśnia to, gdy masz uruchomione bardziej czasochłonne procedury.
Q2: Kiedy zaczyna się P1 Q2 , i dostaje się na miejsce2, uzyskał wyłączną blokadę aktualizacji na poziomie rzędu w obu tabelach broni i szpiegów dla rzędu id=1 (łącznie 2 rzędy, 1 rząd w każdym stole). Jednak P2 nie ma problemów z blokowaniem weapons
w trzecim kwartale , ale P2 ma problemy z blokowaniem w Q4 w miejscu2 spies
.
Tak więc różnice między Q1 i Q2 sprowadzają się do tego, że MySQL wie, że indeks FK nie jest związany z kolumną w UPDATE, a instrukcja stwierdza, że w Note1 poniżej.
Gdy P1 uruchamia Q1, P2 nie ma problemów z zapytaniami typu Q5 bez blokady tylko do odczytu. Jedynymi problemami są to, jakie wersje danych widzi P2 na podstawie obowiązującego POZIOMU IZOLACJI.
Uwaga1 :Ze strony podręcznika MySQL zatytułowanej Blokady ustawione według różnych Instrukcje SQL w InnoDB :
Powyższe jest powodem zachowania Q2: jest taki, że P2 może wykonać UPDATE lub uzyskać wyłączną chwilową blokadę UPDATE na weapons
. Dzieje się tak, ponieważ silnik nie wykonuje UPDATE z P1 na weapon_id i dlatego nie ma blokady na poziomie wiersza w tej tabeli.
Aby sprowadzić to z powrotem do 50 000 stóp, największym zmartwieniem jest czas, w którym blokada jest utrzymywana albo w transakcji niejawnej (bez START/COMMIT), albo w transakcji jawnej przed COMMIT. Procesowi równorzędnemu można teoretycznie zabronić nabywania potrzeby UPDATE w nieskończoność. Ale każda próba uzyskania tej blokady jest zarządzana przez jej ustawienie dla innodb_lock_wait_timeout . Oznacza to, że domyślnie wygasa po około 60 sekundach. Aby zobaczyć swoje ustawienie, uruchom:
select @@innodb_lock_wait_timeout;
Dla mnie w tej chwili jest to 50 (sekund).