SELECCIONE... PARA ACTUALIZAR con ACTUALIZAR
Usando transacciones con InnoDB (compromiso automático desactivado), un SELECT ... FOR UPDATE
permite que una sesión bloquee temporalmente un registro (o registros) en particular para que ninguna otra sesión pueda actualizarlo. Luego, dentro de la misma transacción, la sesión puede realizar una UPDATE
en el mismo registro y confirmar o revertir la transacción. Esto le permitiría bloquear el registro para que ninguna otra sesión pueda actualizarlo mientras tal vez haga alguna otra lógica empresarial.
Esto se logra con el bloqueo. InnoDB utiliza índices para bloquear registros, por lo que bloquear un registro existente parece fácil:simplemente bloquee el índice para ese registro.
SELECCIONE... PARA ACTUALIZAR con INSERTAR
Sin embargo, para usar SELECT ... FOR UPDATE
con INSERT
, ¿cómo bloquea un índice para un registro que aún no existe? Si está utilizando el nivel de aislamiento predeterminado de REPEATABLE READ
, InnoDB también utilizará brecha Cerraduras. Siempre que conozca el id
(o incluso un rango de ID) para bloquear, luego InnoDB puede bloquear el espacio para que no se pueda insertar ningún otro registro en ese espacio hasta que terminemos con él.
Si su id
columna fuera una columna de incremento automático, entonces SELECT ... FOR UPDATE
con INSERT INTO
sería problemático porque no sabría cuál es el nuevo id
fue hasta que lo insertaste. Sin embargo, dado que conoce el id
que desea insertar, SELECT ... FOR UPDATE
con INSERT
funcionará.
ADVERTENCIA
En el nivel de aislamiento predeterminado, SELECT ... FOR UPDATE
en un registro inexistente no bloquear otras transacciones. Entonces, si dos transacciones ambas hacen un SELECT ... FOR UPDATE
en el mismo registro de índice inexistente, ambos obtendrán el bloqueo y ninguna transacción podrá actualizar el registro. De hecho, si lo intentan, se detectará un interbloqueo.
Por lo tanto, si no quiere lidiar con un interbloqueo, puede hacer lo siguiente:
INSERTAR EN...
Inicie una transacción y realice INSERT
. Haga su lógica comercial y confirme o revierta la transacción. Tan pronto como hagas INSERT
en el índice de registro inexistente en la primera transacción, todas las demás transacciones se bloquearán si intentan INSERT
un registro con el mismo índice único. Si la segunda transacción intenta insertar un registro con el mismo índice después de que la primera transacción confirme la inserción, obtendrá un error de "clave duplicada". Manejar en consecuencia.
SELECCIONA... BLOQUEAR EN MODO COMPARTIR
Si selecciona con LOCK IN SHARE MODE
antes del INSERT
, si una transacción anterior ha insertado ese registro pero aún no se ha confirmado, SELECT ... LOCK IN SHARE MODE
se bloqueará hasta que se complete la transacción anterior.
Entonces, para reducir la posibilidad de errores de clave duplicada, especialmente si mantiene los bloqueos por un tiempo mientras realiza la lógica comercial antes de confirmarlos o revertirlos:
SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
- Si no se devolvieron registros, entonces
INSERT INTO FooBar (foo, bar) VALUES (?, ?)