Помислете за следната схема:(Rem stmts оставени за ваше удобство) :
-- 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;
Сега имаме 2 процеса, P1 и P2. Най-добре е да тествате къде P1 е може би MySQL Workbench, а P2 е прозорец на командния ред на MySql. С други думи, трябва да настроите това като отделни връзки и правилно. Ще трябва да имате щателно око, за да ги изпълнявате стъпка по стъпка по правилния начин (описано в Разказ по-долу) и вижте въздействието му върху другия прозорец на процеса.
Разгледайте следните заявки, като имате предвид, че mysql заявка, която не е обвита в изрична транзакция, сама по себе си е имплицитна транзакция. Но по-долу замахнах за изрично:
В1:
START TRANSACTION;
-- place1
UPDATE spies SET name = 'Bond', weapon_id = 1 WHERE id = 1;
-- place2
COMMIT;
В2:
START TRANSACTION;
-- place1
UPDATE spies SET name = 'Bond' WHERE id = 1;
-- place2
COMMIT;
В3:
START TRANSACTION;
-- place1
SELECT id into @mine_to_use from weapons where id=1 FOR UPDATE; -- place2
-- place3
COMMIT;
В4:
START TRANSACTION;
-- place1
SELECT id into @mine_to_use from spies where id=1 FOR UPDATE; -- place2
-- place3
COMMIT;
Q5 (сбор от запитвания):
SELECT * from weapons;
SELECT * from spies;
Разказ
Q1: Когато P1 започне, започва Q1 , и стига до място2, той е получил ексклузивно заключване на актуализация на ниво ред и в двете таблици оръжия и шпиони за id=1 ред (общо 2 реда, 1 ред във всяка таблица). Това може да се докаже, като P2 започва да изпълнява Q3, стига до place1, но блокира на place2 и се освобождава едва когато P1 стигне до извикването на COMMIT. Всичко, което току-що казах за P2, работещо с Q3, е същото и за P2, работещо с Q4. В обобщение, на екрана P2 place2 замръзва до P1 Commit.
Отново бележка относно имплицитните транзакции. Вашият истински Заявката Q1 ще изпълни това много бързо и излизането от нея ще направи имплицитно ангажиране. Предишният абзац обаче го разбива на части, ако трябваше да изпълнявате по-скъпоструващи рутинни процедури.
В2: Когато P1 започне, започва Q2 , и стига до място2, той е получил ексклузивно заключване на актуализация на ниво ред и в двете таблици оръжия и шпиони за id=1 ред (общо 2 реда, 1 ред във всяка таблица). P2 обаче няма проблеми с Q3, блокирайки weapons
, но P2 има проблеми с блокиране, изпълнявайки Q4 на place2 spies
.
И така, разликите между Q1 и Q2 се свеждат до това, че MySQL знае, че FK индексът не е подходящ за колона в АКТУАЛИЗАЦИЯТА, а ръководството посочва това в Забележка1 по-долу.
Когато P1 изпълнява Q1, P2 няма проблеми с типовете заявки само за четене без заключване, придобиващи Q5. Единствените проблеми са какви предавания на данни P2 вижда въз основа на НИВОТО НА ИЗОЛАЦИЯ.
Забележка 1 :От страницата с ръководство за MySQL, озаглавена Заключвания, зададени от различни SQL изрази в InnoDB :
Горното е причината поведението на Q2: е такъв, че P2 е свободен да извърши АКТУАЛИЗАЦИЯ или да придобие ексклузивно АКТУАЛИЗИРАНЕ за моментно заключване на weapons
. Това е така, защото двигателят не извършва АКТУАЛИЗАЦИЯ с P1 на weapon_id и следователно няма заключване на ниво ред в тази таблица.
За да върнете това обратно на 50 000 фута, най-голямото притеснение на човек е продължителността, при която се задържа заключване или в имплицитна транзакция (такава без START/COMMIT), или в изрична транзакция преди COMMIT. На равнопоставен процес може да бъде забранено да придобива нуждата си от АКТУАЛИЗАЦИЯ на теория за неопределено време. Но всеки опит за придобиване на това заключване се управлява от настройката му за innodb_lock_wait_timeout . Това означава, че по подразбиране след около 60 секунди изтича времето. За преглед на вашата настройка, стартирайте:
select @@innodb_lock_wait_timeout;
За мен в момента е 50 (секунди).