MySQL: uma transação bloqueará a linha?

13

Eu não tentei usar a transação MySQL antes, só quero esclarecer uma coisa.

Se dois usuários executam uma consulta no momento exato, como o MySQL lidaria com isso? por exemplo, os usuários estão tentando atualizar um registro.

user1: atualiza o conjunto de tabelas column = column - 4 onde column_id = 1;

user2: atualiza o conjunto de tabelas column = column - 7 em que column_id = 1;

Agora, se eu usar transações, o MySQL escolherá qual consulta será executada primeiro e bloqueará o segundo usuário até que a primeira consulta seja confirmada? Isso será um bloqueio de tabela ou um bloqueio de linha?

E se um terceiro usuário emitir uma instrução select? Qual será o valor que o MySQL retornará?

PS isso será no Innodb.

zer09
fonte

Respostas:

17

Uma única instrução como essa funciona da mesma forma com MyISAM ou InnoDB, com uma transação ou com autocommit = ON. Ele bloqueia o suficiente para fazer a consulta, bloqueando a outra conexão. Quando concluída, a outra conexão continua. Em todos os casos, a coluna é logo diminuída por 11.

Um terceiro usuário pode ver o valor diminuído por 0 ou 4 ou 7 ou 11. O "tempo exato" não é realmente possível porque, em algum momento da execução de cada instrução, um bloqueio de thread único é verificado / definido / o que for . Ou seja, eles serão serializados, tão rápido que você não poderá vê-lo.

O InnoDB bloqueia apenas linhas, não tabelas. (OK, a instrução DDL faz bloqueios mais ousados.)

O que fica mais interessante é uma transação que modifica duas coisas ou que leva uma quantidade notável de tempo:

Caso de intenção: item único, mas demorando:

BEGIN;
SELECT something;
think about it for a while
UPDATE that something;
COMMIT;

O select precisa ser escrito assim:

SELECT something  FOR UPDATE;

Isso informa outras conexões "Pretendo atualizar a linha; por favor, não me atrapalhe". (Trago esse exemplo, porque muitos novatos perdem essa sutileza.)

Caso de impasse: Brincando com duas coisas:

BEGIN;    -- in one connection
UPDATE thing_1;
UPDATE thing_2;
COMMIT;

BEGIN;    -- in another connection, at the "exact same time"
UPDATE thing_2;
UPDATE thing_1;
COMMIT;

Este é o exemplo clássico de um impasse - cada um pega uma coisa e depois pega a outra. Claramente, não pode ser feito para funcionar. Uma transação é morta; o outro termina. Portanto, você deve verificar se há erros para poder descobri-lo.

A reação normal a um impasse é repetir toda a transação com falha. Até então, a outra conexão não estará interferindo e deve prosseguir sem problemas. (OK, outra conexão pode criar outro impasse.)

Caso de atraso: se as duas conexões capturarem várias coisas na mesma ordem, uma poderá ser atrasada até a outra terminar. Para impedir que "espere para sempre", há um padrão de 50 segundos innodb_lock_wait_timeout. Seu par de simples UPDATEsé na verdade um exemplo desse caso. Um terminará prontamente; o outro fica parado até o primeiro término.

Observe como um deadlock pode (em alguns casos) ser transformado em um atraso, ordenando consistentemente as coisas que você toca.

autocommit = 1: com essa configuração e sem chamar BEGIN, cada instrução é efetivamente:

BEGIN;
your statement
COMMIT;

autocommit = 0: este é um problema que espera acontecer. Quando você executa uma consulta de gravação, a BEGINé gerado implicitamente. No entanto, é sua responsabilidade eventualmente emitir COMMIT. Se você não conseguir, você se perguntará por que seu sistema está travado. (Outro erro comum para iniciantes.) Meu conselho: "Nunca use =0".

Rick James
fonte