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
".