sql >> Base de Datos >  >> RDS >> Mysql

Rendimiento de MySQL al actualizar la fila con FK

Considere el siguiente esquema:(Se dejan stmts Rem para su comodidad) :

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

Ahora, tenemos 2 procesos, P1 y P2. Lo mejor es probar dónde P1 es quizás MySQL Workbench y P2 es una ventana de línea de comandos de MySql. En otras palabras, debe configurar esto como conexiones separadas y correctas. Tendría que tener un ojo meticuloso para ejecutarlos paso a paso de la manera adecuada (descrito en la Narrativa a continuación) y vea su impacto en la otra ventana de proceso.

Considere las siguientes consultas, teniendo en cuenta que una consulta mysql que no está incluida en una transacción explícita es en sí misma una transacción implícita. Pero a continuación, opté por explícito:

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 (mezcolanza de consultas):

SELECT * from weapons;
SELECT * from spies;

Narrativa

P1: Cuando P1 comienza a comenzar Q1 , y llega a place2, ha obtenido un bloqueo exclusivo de actualización de nivel de fila en ambas tablas de armas y espías para la fila id=1 (2 filas en total, 1 fila en cada tabla). Esto se puede demostrar si P2 comienza a ejecutar Q3, llega al lugar 1, pero bloquea el lugar 2 y solo se libera cuando P1 llama a COMMIT. Todo lo que acabo de decir sobre P2 ejecutando Q3 es lo mismo para P2 ejecutando Q4. En resumen, en la pantalla P2, place2 se congela hasta que se confirma P1.

Una nota de nuevo sobre las transacciones implícitas. Tu verdadero La consulta Q1 realizará esto muy rápido y al salir de ella se realizará una confirmación implícita. Sin embargo, el párrafo anterior lo desglosa si tiene más rutinas que requieren más tiempo en ejecución.

P2: Cuando P1 comienza a comenzar Q2 , y llega a place2, ha obtenido un bloqueo exclusivo de actualización de nivel de fila en ambas tablas de armas y espías para la fila id=1 (2 filas en total, 1 fila en cada tabla). Sin embargo, P2 no tiene problemas con Q3 bloqueando weapons , pero P2 tiene problemas de bloqueo al ejecutar Q4 en place2 spies .

Entonces, las diferencias entre Q1 y Q2 se reducen a que MySQL sabe que el índice FK no es relevante para una columna en la ACTUALIZACIÓN, y el manual establece que en Nota1 a continuación.

Cuando P1 ejecuta Q1, P2 no tiene problemas con los tipos de consultas Q5 de solo lectura sin bloqueo. Los únicos problemas son las representaciones de datos que ve P2 en función del NIVEL DE AISLAMIENTO establecido.

Nota 1 :De la página del manual de MySQL titulada Locks Set by Different Sentencias SQL en InnoDB :

Lo anterior es por qué el comportamiento de Q2: es tal que P2 es libre de realizar una ACTUALIZACIÓN o adquirir un bloqueo momentáneo exclusivo de ACTUALIZACIÓN en weapons . Esto se debe a que el motor no está realizando una ACTUALIZACIÓN con P1 en weapon_id y, por lo tanto, no tiene un bloqueo de nivel de fila en esa tabla.

Para retroceder esto a 50,000 pies, la mayor preocupación es la duración en la que se mantiene un bloqueo en una transacción implícita (una sin START/COMMIT) o una transacción explícita antes de un COMMIT. Se puede prohibir que un proceso par adquiera su necesidad de una ACTUALIZACIÓN en teoría indefinidamente. Pero cada intento de adquirir ese bloqueo se rige por su configuración para innodb_lock_wait_timeout . Lo que eso significa es que, de forma predeterminada, después de unos 60 segundos, se agota el tiempo de espera. Para ver su configuración, ejecute:

select @@innodb_lock_wait_timeout;

Para mí, en este momento, son 50 (segundos).