sql >> Databáze >  >> RDS >> Mysql

Výkon MySQL při aktualizaci řádku pomocí FK

Zvažte následující schéma:(Ponechané položky pro vaše pohodlí) :

-- 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;

Nyní máme 2 procesy, P1 a P2. Nejlepší je vyzkoušet, kde P1 je možná MySQL Workbench a P2 je okno příkazového řádku MySql. Jinými slovy, musíte to nastavit jako samostatná připojení a správně. Museli byste mít pečlivé oko, abyste je krok za krokem provedli správným způsobem (popsáno v Příběhu níže) a podívejte se na jeho dopad na další okno procesu.

Zvažte následující dotazy a mějte na paměti, že dotaz mysql, který není zabalen do explicitní transakce, je sám o sobě implicitní transakcí. Ale níže jsem se obrátil na explicitní:

Q1:

START TRANSACTION;
-- place1
UPDATE spies SET name = 'Bond', weapon_id = 1 WHERE id = 1;
-- place2
COMMIT;

Q2:

START TRANSACTION;
-- place1
UPDATE spies SET name = 'Bond' WHERE id = 1;
-- place2
COMMIT;

Q3:

START TRANSACTION;
-- place1
SELECT id into @mine_to_use from weapons where id=1 FOR UPDATE; -- place2
-- place3
COMMIT;

Q4:

START TRANSACTION;
-- place1
SELECT id into @mine_to_use from spies where id=1 FOR UPDATE; -- place2
-- place3
COMMIT;

Q5 (hodge podge dotazů):

SELECT * from weapons;
SELECT * from spies;

Příběh

O1: Když P1 začne začínat Q1 a dostane se na místo2, získal exkluzivní zámek aktualizace na úrovni řádku v obou tabulkách zbraně a špioni pro id=1 řádek (2 řádky celkem, 1 řádek v každé tabulce). To lze dokázat tak, že P2 začne spouštět Q3, dostane se na místo 1, ale zablokuje se na místě 2 a bude uvolněn, až když P1 zavolá COMMIT. Všechno, co jsem právě řekl o P2 běžícím Q3, platí pro P2 běžícím Q4. Stručně řečeno, na obrazovce P2 místo2 zamrzne, dokud nebude P1 Commit.

Ještě jedna poznámka o implicitních transakcích. Vaše skutečné Dotaz Q1 to provede velmi rychle a jeho výstup provede implicitní potvrzení. Předchozí odstavec to však rozebírá, pokud máte spuštěny časově náročnější rutiny.

2. otázka: Když P1 začne začínat Q2 a dostane se na místo2, získal exkluzivní zámek aktualizace na úrovni řádku v obou tabulkách zbraně a špioni pro id=1 řádek (2 řádky celkem, 1 řádek v každé tabulce). P2 však nemá žádné problémy s blokováním weapons ve 3. čtvrtletí , ale P2 má problémy se spuštěním Q4 na place2 spies .

Takže rozdíly mezi Q1 a Q2 se týkají MySQL, protože ví, že FK index není relevantní pro sloupec v UPDATE, a manuál uvádí, že v Note1 níže.

Když P1 spustí Q1, P2 nemá žádné problémy s typy dotazů Q5 bez uzamčení pouze pro čtení. Jediným problémem je, jaká data P2 vidí na základě zavedené ÚROVNĚ IZOLACE.

Poznámka 1 :Z manuálové stránky MySQL nazvané Zámky nastavené různými Příkazy SQL v InnoDB :

Výše uvedené je důvodem chování Q2: je takový, že P2 může volně provádět UPDATE nebo získat UPDATE exkluzivní dočasné uzamčení weapons . Je to proto, že engine neprovádí UPDATE s P1 na id_zbroje, a proto v této tabulce nemá zámek na úrovni řádku.

Abychom to vrátili zpět na 50 000 stop, největší starostí je doba, po kterou je zámek držen buď v implicitní transakci (bez START/COMMIT), nebo v explicitní transakci před COMMIT. Peer procesu může být teoreticky zakázáno získávat jeho potřebu AKTUALIZACE na dobu neurčitou. Ale každý pokus o získání tohoto zámku se řídí jeho nastavením pro innodb_lock_wait_timeout . To znamená, že ve výchozím nastavení vyprší časový limit asi po 60 sekundách. Chcete-li zobrazit své nastavení, spusťte:

select @@innodb_lock_wait_timeout;

Pro mě je to v tuto chvíli 50 (sekund).



  1. Použití pdo v php s uloženou procedurou

  2. Získejte maximální hodnotu a odpovídající sloupec

  3. Dočasná sekvence v rámci SELECT

  4. Vypsat všechny cizí klíče PostgreSQL