Mysql
 sql >> Database >  >> RDS >> Mysql

Prestazioni MySQL durante l'aggiornamento della riga con FK

Considera il seguente schema:(Rem stmts lasciato per comodità) :

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

Ora abbiamo 2 processi, P1 e P2. Meglio testare dove P1 è forse MySQL Workbench e P2 è una finestra della riga di comando di MySql. In altre parole, devi configurarlo come connessioni separate e corrette. Dovresti avere un occhio meticoloso per eseguirli passo dopo passo nel modo corretto (descritto nella Narrativa sotto) e vedere il suo impatto sull'altra finestra del processo.

Considera le seguenti query, tenendo presente che una query MySQL non inclusa in una transazione esplicita è essa stessa una transazione implicita. Ma sotto, ho oscillato per esplicito:

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 (guazzabuglio di query):

SELECT * from weapons;
SELECT * from spies;

Narrativa

Q1: Quando P1 inizia a iniziare il Q1 , e arriva a place2, ha ottenuto un blocco di aggiornamento esclusivo a livello di riga in entrambe le tabelle armi e spie per la riga id=1 (2 righe in totale, 1 riga in ogni tabella). Ciò può essere dimostrato da P2 che inizia a correre Q3, arrivando alla posizione 1, ma bloccando sulla posizione 2 e liberandosi solo quando P1 riesce a chiamare COMMIT. Tutto ciò che ho appena detto sulla P2 che esegue la Q3 è idem per la P2 che esegue la Q4. In sintesi, nella schermata P2, place2 si blocca fino al commit P1.

Ancora una nota sulle transazioni implicite. Il tuo vero La query Q1 verrà eseguita molto velocemente e in uscita verrà eseguito un commit implicito. Tuttavia, il paragrafo precedente lo scompone se dovessi eseguire routine più costose in termini di tempo.

Q2: Quando P1 inizia a iniziare il Q2 , e arriva a place2, ha ottenuto un blocco di aggiornamento esclusivo a livello di riga in entrambe le tabelle armi e spie per la riga id=1 (2 righe in totale, 1 riga in ogni tabella). Tuttavia, P2 non ha problemi con il blocco di weapons del terzo trimestre , ma P2 ha problemi di blocco durante l'esecuzione di Q4 su place2 spies .

Quindi, le differenze tra Q1 e Q2 derivano da MySQL sapendo che l'indice FK non è rilevante per una colonna nell'UPDATE e il manuale afferma che in Note1 sotto.

Quando P1 esegue Q1, P2 non ha problemi con i tipi di query Q5 di sola lettura senza blocco. Gli unici problemi sono ciò che le rappresentazioni dei dati vedono P2 in base al LIVELLO DI ISOLAMENTO in atto.

Nota1 :Dalla pagina del manuale MySQL intitolata Blocchi impostati da diversi Istruzioni SQL in InnoDB :

Quanto sopra è il motivo per cui il comportamento di Q2: è tale che P2 è libero di eseguire un UPDATE o acquisire un blocco momentaneo esclusivo UPDATE su weapons . Questo perché il motore non esegue un UPDATE con P1 su Weapon_id e quindi non ha un blocco a livello di riga in quella tabella.

Per riportarlo a 50.000 piedi, la più grande preoccupazione è la durata in cui un blocco viene mantenuto in una transazione implicita (una senza START/COMMIT) o in una transazione esplicita prima di un COMMIT. In teoria, a un processo peer può essere vietato acquisire la necessità di un AGGIORNAMENTO a tempo indeterminato. Ma ogni tentativo di acquisire quel blocco è regolato dalla sua impostazione per innodb_lock_wait_timeout . Ciò significa che, per impostazione predefinita, dopo circa 60 secondi scade. Per visualizzare le tue impostazioni, esegui:

select @@innodb_lock_wait_timeout;

Per me, al momento, sono 50 (secondi).