MySQL: ¿una transacción bloqueará la fila?


13

No he intentado usar la transacción MySQL antes, solo quiero aclarar algo.

Si dos usuarios ejecutan una consulta en el momento exacto, ¿cómo manejaría esto MySQL? por ejemplo, los usuarios están tratando de actualizar un registro.

usuario1: conjunto de tablas de actualización columna = columna - 4 donde column_id = 1;

usuario2: conjunto de tablas de actualización columna = columna - 7 donde column_id = 1;

Ahora, si uso transacciones, ¿elegirá MySQL qué consulta se ejecutará primero y bloqueará al segundo usuario hasta que se confirme la primera consulta? ¿Será un bloqueo de mesa o un bloqueo de fila?

¿Qué pasa si un tercer usuario emitirá una declaración select? ¿Cuál será el valor que devolverá MySQL?

PD: esto será en Innodb.

Respuestas:


17

Una sola declaración como esa funciona igual con MyISAM o InnoDB, con una transacción o con autocommit = ON. Bloquea lo suficiente como para hacer la consulta, bloqueando así la otra conexión. Cuando termine, la otra conexión continúa. En todos los casos, la columna pronto se reduce en 11.

Un tercer usuario puede ver el valor disminuido en 0 o 4 o 7 u 11. El "tiempo muy exacto" no es realmente posible porque, en algún momento de la ejecución de cada instrucción, se verifica / establece un bloqueo de un solo subproceso / lo que sea . Es decir, que se pueden serializar, tan rápido que no puede verlo.

InnoDB bloquea solo filas, no tablas. (OK, la declaración DDL hace bloqueos más audaces).

Lo que se vuelve más interesante es una transacción que modifica dos cosas, o que lleva una cantidad de tiempo notable:

Caso de intención: artículo único pero tomando tiempo:

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

El select debe escribirse así:

SELECT something  FOR UPDATE;

Esto le dice a otras conexiones "Tengo la intención de actualizar la fila; por favor, no me confundas". (Menciono este ejemplo, porque muchos novatos extrañan esta sutileza).

Caso de punto muerto: jugando con 2 cosas:

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 es el ejemplo clásico de un punto muerto: cada uno toma una cosa y luego alcanza la otra. Claramente no se puede hacer que funcione. Se mata una transacción; el otro completa. Por lo tanto, debe verificar los errores para poder descubrirlo.

La reacción normal a un punto muerto es reproducir toda la transacción fallida. Para entonces, la otra conexión no interferirá, y debe continuar sin problemas. (OK, otra conexión podría crear otro punto muerto).

Caso de retraso: si las dos conexiones toman varias cosas en el mismo orden, entonces una puede demorarse hasta que la otra termine. Para evitar que esto "espere para siempre", hay un valor predeterminado de 50 segundos innodb_lock_wait_timeout. Su par de simples UPDATEses en realidad un ejemplo de este caso. Uno terminará puntualmente; el otro se detiene hasta que termina el primero.

Observe cómo un punto muerto puede (en algunos casos) convertirse en un retraso al ordenar constantemente las cosas que toca.

autocommit = 1: con esta configuración y sin llamar BEGIN, cada declaración es efectiva:

BEGIN;
your statement
COMMIT;

autocommit = 0: este es un problema esperando que suceda. Cuando realiza una consulta de escritura, BEGINse genera implícitamente a. Sin embargo, es su responsabilidad emitir eventualmente COMMIT. Si no lo hace, se preguntará por qué su sistema está bloqueado. (Otro error de novato común). Mi consejo: "Nunca usar =0".

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.