sistemas - ¿Cómo asegurarse de que no haya una condición de carrera en la base de datos MySQL al incrementar un campo?
race condition vulnerability (3)
Aquí hay 3 enfoques diferentes:
Actualización atómica
update table set tries=tries+1 where condition=value;
y se hará atómicamente.
Utilizar transacciones
Si necesita primero seleccionar el valor y actualizarlo en su aplicación, es probable que necesite usar transacciones. Eso significa que tendrás que usar InnoDB, no tablas MyISAM. Su consulta sería algo como:
BEGIN; //or any method in the API you use that starts a transaction
select tries from table where condition=value for update;
.. do application logic to add to `tries`
update table set tries=newvalue where condition=value;
END;
Si la transacción falla, es posible que deba volver a intentarlo manualmente.
Esquema de version
Un enfoque común es introducir una columna de versión en su tabla. Tus consultas harían algo como:
select tries,version from table where condition=value;
.. do application logic, and remember the old version value.
update table set tries=newvalue,version=version + 1 where condition=value and version=oldversion;
Si esa actualización falla / devuelve 0 filas afectadas, alguien más ha actualizado la tabla mientras tanto. Debe comenzar de nuevo, es decir, seleccionar los nuevos valores, aplicar la lógica de la aplicación e intentar la actualización nuevamente.
¿Cómo prevenir una condición de carrera en la base de datos MySQL cuando dos conexiones quieren actualizar el mismo registro?
Por ejemplo, la conexión 1 quiere aumentar el contador de "intentos". Y la segunda conexión quiere hacer lo mismo. Ambas conexiones SELECT
el conteo de "intentos", aumentan el valor y ambos UPDATE
"intentos" con el valor aumentado. De repente, "intentos" es solo "intentos +1" en lugar de ser "intentos +2", porque ambas conexiones obtuvieron los mismos "intentos" y lo incrementaron en uno.
¿Cómo resolver este problema?
Le sugiero que busque el tema: "Bloquear en la tabla" función Mysql. Bloquear una mesa permite resolver este tipo de situaciones.
Use una sola declaración en lugar de dos. Una sola instrucción de UPDATE
que realice tanto la lectura como la escritura será atómica y no entrará en conflicto con otra actualización simultánea.
UPDATE table SET tries = tries + 1 WHERE ...
O puede usar transacciones para hacer las dos operaciones atómicas.
BEGIN
SELECT ...
UPDATE ...
COMMIT
O, de manera más primitiva, cierra la mesa mientras le lees / escribes.
LOCK TABLES table WRITE
SELECT ...
UPDATE ...
UNLOCK TABLES