2010-11-19 30 views
84

Estoy un poco confundido con las transacciones frente a las tablas de bloqueo para garantizar la integridad de la base de datos y asegurarme de que SELECT y UPDATE permanecen sincronizados y ninguna otra conexión interfiere con él. Necesito:MySQL: Transacciones vs Tablas de bloqueo

SELECT * FROM table WHERE (...) LIMIT 1 

if (condition passes) { 
    // Update row I got from the select 
    UPDATE table SET column = "value" WHERE (...) 

    ... other logic (including INSERT some data) ... 
} 

necesito para asegurarse de que no hay otras consultas interferirán y realizar la misma SELECT (Leyendo el 'valor antiguo' antes de que Connection termine la actualización de la fila

Sé que puedo por defecto. a LOCK TABLES table para asegurarte de que solo 1 conexión está haciendo esto a la vez, y desbloquearlo cuando haya terminado, pero eso parece exagerado. Sería bueno hacer eso en una transacción (asegurando que ninguna otra conexión intente lo mismo proceso mientras otro todavía está procesando)? O sería un SELECT ... FOR UPDATE o SELECT ... LOCK IN SHARE MODE mejor?

Respuesta

124

tablas de bloqueo impide que otros usuarios DB afecte a las filas/tablas que has bloqueado. Pero los bloqueos, por sí mismos, NO garantizarán que su lógica salga en un estado consistente.

Piense en un sistema bancario.Cuando paga una factura en línea, hay al menos dos cuentas afectadas por la transacción: su cuenta, de la cual se toma el dinero. Y la cuenta del receptor, en la que se transfiere el dinero. Y la cuenta del banco, en la que felizmente depositarán todas las tarifas de servicio cargadas en la transacción. Teniendo en cuenta (como todo el mundo sabe en estos días) que los bancos son extraordinariamente estúpida, digamos que su sistema funciona así:

$balance = "GET BALANCE FROM your ACCOUNT"; 
if ($balance < $amount_being_paid) { 
    charge_huge_overdraft_fees(); 
} 
$balance = $balance - $amount_being paid; 
UPDATE your ACCOUNT SET BALANCE = $balance; 

$balance = "GET BALANCE FROM receiver ACCOUNT" 
charge_insane_transaction_fee(); 
$balance = $balance + $amount_being_paid 
UPDATE receiver ACCOUNT SET BALANCE = $balance 

Ahora, sin cerraduras y no hay transacciones, este sistema es vulnerable a diversas condiciones de carrera, el más grande de que son pagos múltiples que se realizan en su cuenta, o la cuenta del receptor en paralelo. Si bien su código ha recuperado su saldo y está haciendo huge_overdraft_fees() y lo que sea, es muy posible que algún otro pago ejecute el mismo tipo de código en paralelo. Recuperarán su saldo (digamos, $ 100), realizarán sus transacciones (saquen los $ 20 que están pagando y los $ 30 que les están gastando), y ahora ambos caminos de código tienen dos saldos diferentes: $ 80 y $ 70. Dependiendo de cuáles finalizan, terminará con cualquiera de esos dos saldos en su cuenta, en lugar de los $ 50 que debería haber terminado ($ 100 - $ 20 - $ 30). En este caso, "error bancario a su favor".

Ahora, digamos que usa bloqueos. Su pago de factura ($ 20) llega primero a la tubería, por lo que gana y bloquea el registro de su cuenta. Ahora tiene uso exclusivo y puede deducir los $ 20 del saldo, y devolver el nuevo saldo en paz ... y su cuenta termina con $ 80 como se esperaba. Pero ... uhoh ... Intentas actualizar la cuenta del receptor, y está bloqueado, y bloqueado más de lo que permite el código, agotando tu transacción ... Estamos lidiando con bancos estúpidos, así que en lugar de tener el error correcto manejo, el código simplemente saca un exit(), y tus $ 20 desaparecen en una nube de electrones. Ahora está ganando $ 20, y aún le debe $ 20 al receptor, y su teléfono se vuelve a embargar.

Entonces ... ingrese las transacciones. Empieza una transacción, debita su cuenta $ 20, intenta darle crédito al receptor con $ 20 ... y algo explota de nuevo. Pero esta vez, en lugar de exit(), el código puede simplemente hacer rollback, y poof, tus $ 20 se agregarán mágicamente a tu cuenta.

Al final, todo se reduce a esto:

Cerraduras mantener cualquier otra persona interfiera con ningún registros de la base que está tratando. Las transacciones evitan que los errores "posteriores" interfieran con las acciones "anteriores" que haya realizado. Ninguno de los dos puede garantizar que las cosas funcionen bien al final. Pero juntos, lo hacen.

en la lección de mañana: The Joy of Deadlocks.

+2

También estoy/todavía estoy confundido. Supongamos que la cuenta del receptor tenía $ 100 para comenzar y estamos agregando el pago de $ 20 en nuestra cuenta. Mi comprensión de las transacciones es que cuando comienzan, cualquier operación dentro de la transacción ve la base de datos en el estado que tenía al comienzo de la transacción. es decir: hasta que lo modifiquemos, la cuenta del receptor tiene $ 100. Entonces ... cuando agregamos $ 20, en realidad establecemos un saldo de $ 120. Pero, ¿qué sucede si, durante nuestra transacción, alguien agotó la cuenta del receptor a $ 0? ¿Esto es prevenido de alguna manera? ¿Obtienen mágicamente $ 120 de nuevo? ¿Es por esto que también se necesitan los bloqueos? – Russ

+0

Sí, ahí es donde entran en juego los bloqueos. Un sistema adecuado escribiría-bloquearía el registro para que nadie más pudiera actualizar el registro mientras la transacción avanza. Un sistema paranoico pondría un bloqueo incondicional en el registro para que nadie pueda leer el balance "obsoleto" tampoco. –

+1

Básicamente mira las transacciones como asegurando cosas dentro de la ruta del código. Bloquea cosas seguras en rutas de código "paralelas". Hasta que lleguen los bloqueos ... –

0

que haría uso de un

START TRANSACTION WITH CONSISTENT SNAPSHOT; 

para empezar, y una

COMMIT; 

para terminar con.

Todo lo que haga en el medio está aislado de los demás usuarios de su base de datos si su motor de almacenamiento admite transacciones (que es InnoDB).

+1

Excepto que la tabla que está seleccionando no se bloqueará a otras sesiones a menos que específicamente la bloquee (o hasta que su ACTUALIZACIÓN ocurra), lo que significa que otras sesiones podrían aparecer y modificarse entre SELECT y UPDATE. –

+0

Después de leer START TRANSACTION WITH COHERSTAR SNAPSHOT en la documentación de MySQL, no veo dónde se bloquea realmente otra conexión de actualizar la misma fila. Tengo entendido que vería sin embargo que la mesa comenzó al comienzo de la transacción. Entonces, si otra transacción está en progreso, ya ha recibido una fila y está a punto de actualizarla, la segunda transacción aún verá la fila antes de que se haya actualizado. Por lo tanto, podría intentar actualizar la misma fila a la que está haciendo la otra transacción. ¿Es correcto o me estoy perdiendo algo en el progreso? – Ryan

+1

@Ryan No hace ningún bloqueo; estás en lo correcto. El bloqueo (o no) está determinado por el tipo de operaciones que realiza (SELECT/UPDATE/DELETE). –

6

Tuve un problema similar al intentar un IF NOT EXISTS ... y luego realizar un INSERT que causó una condición de carrera cuando varios hilos estaban actualizando la misma tabla.

he encontrado la solución al problema aquí: How to write INSERT IF NOT EXISTS queries in standard SQL

Me di cuenta que no responde directamente a su pregunta, pero el mismo principio de realizar una comprobación e insertar como una sola declaración es muy útil; deberías poder modificarlo para realizar tu actualización.

+1

+1 para un excelente artículo vinculado independiente de la plataforma. – Russ

13

Quiere una SELECT ... FOR UPDATE o SELECT ... LOCK IN SHARE MODE dentro de una transacción, como usted dijo, ya que normalmente los SELECT, sin importar si están en una transacción o no, no bloquearán una tabla. El que elija dependerá de si desea que otras transacciones puedan leer esa fila mientras su transacción está en progreso.

http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

START TRANSACTION WITH CONSISTENT SNAPSHOT no va a hacer el truco para usted, como otras transacciones todavía pueden venir y modificar esa fila. Esto se menciona justo en la parte superior del enlace a continuación.

Si otras sesiones a la vez actualización de la misma mesa [...] que pueden ver la tabla en un estado que no existía en la base de datos.

http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html

2

Se le confunde con la transacción de bloqueo &. Son dos cosas diferentes en RMDB. El bloqueo impide las operaciones simultáneas mientras que la transacción se centra en el aislamiento de datos. Echa un vistazo a this gran artículo para la aclaración y una solución elegante.

+0

Los bloqueos evitan que otros interfieran con los registros con los que está trabajando describe brevemente lo que hace, y las transacciones previenen errores posteriores (los de otros que realizan cambios en paralelo) interfieren con cosas anteriores (al permitir retroceder en caso de que alguien lo hiciera) algo en paralelo) resume bastante las transacciones ... ¿qué se confunde con su comprensión de estos temas? – steviesama

2

Los conceptos de transacción y los bloqueos son diferentes. Sin embargo, la transacción usó bloqueos para ayudarlo a seguir los principios de ACID. Si desea evitar que otros lean/escriban en el mismo punto de tiempo mientras lee/escribe, necesita un candado para hacerlo. Si desea asegurarse de la integridad y consistencia de los datos, es mejor que use las transacciones. Creo que los conceptos combinados de los niveles de aislamiento en las transacciones con bloqueos. Busque los niveles de aislamiento de las transacciones, SERIALIZE debe ser el nivel que desee.