Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Desempenho do MySQL ao atualizar a linha com FK


Considere o seguinte esquema:(Rem stmts deixados para sua conveniência) :
-- 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;

Agora, temos 2 processos, P1 e P2. Melhor testar onde P1 é talvez MySQL Workbench e P2 é uma janela de linha de comando MySql. Em outras palavras, você tem que configurar isso como conexões separadas e corretas. Você teria que ter um olhar meticuloso para executá-los passo a passo da maneira adequada (descrito na Narrativa abaixo) e veja seu impacto na outra janela do processo.

Considere as seguintes consultas, tendo em mente que uma consulta mysql não envolvida em uma transação explícita é ela mesma uma transação implícita. Mas abaixo, eu balancei para explícito:

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 de consultas):
SELECT * from weapons;
SELECT * from spies;

Narrativa


Q1: Quando o P1 começa a começar Q1 , e chega a place2, obteve um bloqueio exclusivo de atualização em nível de linha em ambas as tabelas armas e espiões para a linha id=1 (2 linhas no total, 1 linha em cada tabela). Isso pode ser provado por P2 começando a executar Q3, chegando a place1, mas bloqueando em place2, e só sendo liberado quando P1 chega a chamar COMMIT. Tudo o que acabei de dizer sobre P2 rodando Q3 é idem para P2 rodando Q4. Em resumo, na tela P2, place2 congela até o P1 Commit.

Uma nota novamente sobre transações implícitas. Seu verdadeiro A consulta Q1 vai realizar isso muito rápido e sair dela fará um commit implícito. No entanto, o parágrafo anterior divide tudo se você tiver rotinas mais caras em execução.

T2: Quando o P1 começa a começar Q2 , e chega a place2, obteve um bloqueio exclusivo de atualização em nível de linha em ambas as tabelas armas e espiões para a linha id=1 (2 linhas no total, 1 linha em cada tabela). No entanto, o P2 não tem problemas com o bloqueio de weapons no terceiro trimestre , mas P2 tem problemas de bloco executando Q4 em place2 spies .

Então, as diferenças entre Q1 e Q2 se resumem ao MySQL sabendo que o índice FK não é relevante para uma coluna no UPDATE, e o manual afirma que em Nota1 abaixo de.

Quando P1 executa Q1, P2 não tem problemas com os tipos de consultas Q5 sem bloqueio somente leitura. Os únicos problemas são quais renderizações de dados P2 vê com base no NÍVEL DE ISOLAMENTO em vigor.

Nota1 :Da página de manual do MySQL intitulada Locks Set by Different Instruções SQL no InnoDB :

O acima é o motivo do comportamento do Q2: é tal que P2 é livre para realizar um UPDATE ou adquirir um bloqueio momentâneo exclusivo de UPDATE em weapons . Isso ocorre porque o mecanismo não está executando um UPDATE com P1 em weapon_id e, portanto, não possui um bloqueio de nível de linha nessa tabela.

Para puxar isso de volta para 50.000 pés, a maior preocupação é a duração na qual um bloqueio é mantido em uma transação implícita (uma sem START/COMMIT) ou em uma transação explícita antes de um COMMIT. Um processo de mesmo nível pode ser proibido de adquirir sua necessidade de UPDATE em teoria indefinidamente. Mas cada tentativa de adquirir esse bloqueio é regida por sua configuração para innodb_lock_wait_timeout . O que isso significa é que, por padrão, após cerca de 60 segundos, ele expira. Para uma visualização da sua configuração, execute:
select @@innodb_lock_wait_timeout;

Para mim, no momento, são 50 (segundos).