Bloquear tablas evita que otros usuarios de la base de datos afecten las filas / tablas que ha 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, a la que se transfiere el dinero. Y la cuenta del banco, en la que depositarán felizmente todas las tarifas de servicio cobradas en la transacción. Dado (como todos saben estos días) que los bancos son extraordinariamente estúpidos, 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 bloqueos ni transacciones, este sistema es vulnerable a varias condiciones de carrera, la mayor de las cuales son los pagos múltiples que se realizan en su cuenta o la cuenta del receptor en paralelo. Si bien su código tiene su saldo recuperado y está haciendo el enorme_sobredraft_fees () y todo eso, es muy posible que algún otro pago ejecute el mismo tipo de código en paralelo. Recuperarán su saldo (digamos, $ 100), harán sus transacciones (sacarán los $ 20 que está pagando y los $ 30 con los que lo están jodiendo), y ahora ambas rutas de código tienen dos saldos diferentes: $ 80 y $ 70. Dependiendo de cuáles terminen en último lugar, 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 candados. El pago de su 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 escribir el nuevo saldo en paz ... y su cuenta termina con $ 80 como se esperaba. Pero ... uhoh ... Intentas actualizar la cuenta del destinatario, y está bloqueada, y bloqueada más de lo que permite el código, agota el tiempo de tu transacción ... Estamos tratando con bancos estúpidos, así que en lugar de tener el error adecuado manejo, el código simplemente extrae unexit()
, y sus $ 20 se desvanecen en una nube de electrones. Ahora te quedaste con $ 20 y todavía le debes $ 20 al receptor, y tu teléfono es embargado.
Entonces ... ingrese transacciones. Comienzas una transacción, cargas en tu cuenta $ 20, intentas acreditar al receptor con $ 20 ... y algo vuelve a explotar. Pero esta vez, en lugar de exit()
, el código puede hacerrollback
, y puf, tus $ 20 se agregan mágicamente a tu cuenta.
Al final, se reduce a esto:
Los bloqueos evitan que nadie más interfiera con los registros de la base de datos con los que está tratando. Las transacciones evitan que los errores "posteriores" interfieran con las cosas "anteriores" que ha realizado. Ninguno de los dos por sí solo puede garantizar que las cosas salgan bien al final. Pero juntos lo hacen.
en la lección de mañana: The Joy of Deadlocks.
Quiere un
SELECT ... FOR UPDATE
oSELECT ... LOCK IN SHARE MODE
dentro de una transacción, como 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 curso.http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
START TRANSACTION WITH CONSISTENT SNAPSHOT
no hará el truco por usted, ya que otras transacciones aún pueden aparecer y modificar esa fila. Esto se menciona en la parte superior del enlace a continuación.http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html
fuente
Los conceptos de transacción y los bloqueos son diferentes. Sin embargo, la transacción utilizó bloqueos para ayudarla a seguir los principios de ACID. Si desea que la tabla evite que otros lean / escriban al mismo tiempo mientras usted lee / escribe, necesita un candado para hacer esto. Si desea asegurarse de la integridad y consistencia de los datos, es mejor que utilice las transacciones. Creo que conceptos mixtos de niveles de aislamiento en transacciones con bloqueos. Busque niveles de aislamiento de transacciones, SERIALIZE debe ser el nivel que desee.
fuente
Tuve un problema similar al intentar una
IF NOT EXISTS ...
y luego realizar unaINSERT
que causó una condición de carrera cuando varios subprocesos estaban actualizando la misma tabla.Encontré la solución al problema aquí: Cómo escribir consultas INSERT IF NOT EXISTS en SQL estándar
Me doy cuenta de que esto no responde directamente a su pregunta, pero el mismo principio de realizar una verificación e insertar como una sola declaración es muy útil; debería poder modificarlo para realizar su actualización.
fuente
Estás confundido con bloqueo y transacción. Son dos cosas diferentes en RMDB. El bloqueo evita operaciones simultáneas mientras que la transacción se centra en el aislamiento de datos. Consulte este gran artículo para obtener una aclaración y una solución elegante.
fuente
Usaría un
para empezar, y un
para terminar.
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).
fuente