Ayúdame a comprender el caso de uso subyacente SELECT ... FOR UPDATE
.
Pregunta 1 : ¿Es el siguiente un buen ejemplo de cuándo SELECT ... FOR UPDATE
debe usarse?
Dado:
- habitaciones [id]
- etiquetas [id, nombre]
- room_tags [room_id, tag_id]
- room_id y tag_id son claves externas
La aplicación quiere enumerar todas las habitaciones y sus etiquetas, pero necesita diferenciar las habitaciones sin etiquetas de las habitaciones que se han eliminado. Si SELECT ... FOR UPDATE no se usa, lo que podría suceder es:
- Inicialmente:
- habitaciones contiene
[id = 1]
- etiquetas contiene
[id = 1, name = 'cats']
- room_tags contiene
[room_id = 1, tag_id = 1]
- habitaciones contiene
- Hilo 1:
SELECT id FROM rooms;
returns [id = 1]
- Hilo 2:
DELETE FROM room_tags WHERE room_id = 1;
- Hilo 2:
DELETE FROM rooms WHERE id = 1;
- Hilo 2: [confirma la transacción]
- Hilo 1:
SELECT tags.name FROM room_tags, tags WHERE room_tags.tag_id = 1 AND tags.id = room_tags.tag_id;
- devuelve una lista vacía
Ahora el hilo 1 piensa que la habitación 1 no tiene etiquetas, pero en realidad la habitación se ha eliminado. Para resolver este problema, el hilo 1 debería SELECT id FROM rooms FOR UPDATE
, evitando así que el hilo 2 se elimine rooms
hasta que el hilo 1 esté terminado. ¿Es eso correcto?
Pregunta 2 : ¿Cuándo se debe utilizar el SERIALIZABLE
aislamiento de transacciones frente a READ_COMMITTED
con SELECT ... FOR UPDATE
?
Se espera que las respuestas sean portátiles (no específicas de la base de datos). Si eso no es posible, explique por qué.
REPEATABLE_READ
eREAD_COMMITTED
incluso las opciones portátiles? Los únicos resultados que obtengo son para el servidor MSSQLREAD COMMITTED
modo no define si realmente verá o no los registros comprometidos por otra transacción: solo se asegura de que nunca verá registros no confirmados.select ... for update
onrooms
todavía permitirároom_tags
que se eliminen porque son tablas separadas. ¿Quería preguntar si lafor update
cláusula evitará las eliminacionesrooms
?Respuestas:
La única forma portátil de lograr la coherencia entre las habitaciones y las etiquetas y asegurarse de que las habitaciones nunca se devuelvan después de haber sido eliminadas es bloquearlas con
SELECT FOR UPDATE
.Sin embargo, en algunos sistemas el bloqueo es un efecto secundario del control de simultaneidad y se obtienen los mismos resultados sin especificar
FOR UPDATE
explícitamente.Esto depende del control de concurrencia que esté usando su sistema de base de datos.
MyISAM
inMySQL
(y varios otros sistemas antiguos) bloquea toda la tabla durante una consulta.En
SQL Server
, lasSELECT
consultas colocan bloqueos compartidos en los registros / páginas / tablas que han examinado, mientras que lasDML
consultas colocan bloqueos de actualización (que luego son promovidos a exclusivos o degradados a bloqueos compartidos). Los bloqueos exclusivos son incompatibles con los bloqueos compartidos, por lo queSELECT
o laDELETE
consulta se bloqueará hasta que se confirme otra sesión.En las bases de datos cuyo uso
MVCC
(comoOracle
,PostgreSQL
,MySQL
conInnoDB
), unaDML
consulta crea una copia del registro (en uno u otro sentido) y, en general los lectores no bloquean escritores y viceversa. Para estas bases de datos,SELECT FOR UPDATE
sería útil: bloquearíaSELECT
o laDELETE
consulta hasta que se confirme otra sesión, al igual que loSQL Server
hace.Generalmente,
REPEATABLE READ
no prohíbe las filas fantasma (filas que aparecieron o desaparecieron en otra transacción, en lugar de ser modificadas)En
Oracle
yPostgreSQL
versiones anteriores ,REPEATABLE READ
es en realidad un sinónimo deSERIALIZABLE
. Básicamente, esto significa que la transacción no ve los cambios realizados después de que ha comenzado. Entonces, en esta configuración, la últimaThread 1
consulta devolverá la habitación como si nunca se hubiera eliminado (que puede ser o no lo que deseaba). Si no desea mostrar las habitaciones después de que se hayan eliminado, debe bloquear las filas conSELECT FOR UPDATE
En
InnoDB
,REPEATABLE READ
ySERIALIZABLE
son cosas diferentes: los lectores enSERIALIZABLE
modo establecen bloqueos de siguiente clave en los registros que evalúan, evitando de manera efectiva la concurrenciaDML
en ellos. Por lo tanto, no necesita unSELECT FOR UPDATE
modo serializable, pero sí los necesita enREPEATABLE READ
oREAD COMMITED
.Tenga en cuenta que el estándar sobre modos de aislamiento prescribe que no ve ciertas peculiaridades en sus consultas, pero no define cómo (con bloqueo o con
MVCC
o de otro modo).Cuando digo "no es necesario
SELECT FOR UPDATE
", debería haber agregado "debido a los efectos secundarios de la implementación de cierto motor de base de datos".fuente
SERIALIZABLE
debería usarse versusREAD_COMMITTED
conSELECT ... FOR UPDATE
. ¿Puede actualizar su respuesta para reflejar esta pregunta actualizada?SELECT FOR UPDATE
en modo serializable", conInnoDB
. Con los otrosMVCC
sistemas, los dos son sinónimos y los necesitaSELECT FOR UPDATE
.This depends on the concurrency control your database system is using
: Creo que te estás partiendo los pelos. Todos los casos que enumera a continuación dicen que la sala no se eliminaSELECT
hasta el final de la transacción. Entonces, ¿no debería la respuesta simplementeYes
con las referencias de apoyo a continuación?Respuestas cortas:
Q1: Sí.
P2: No importa cuál uses.
Respuesta larga:
A
select ... for update
(como implica) seleccionará ciertas filas pero también las bloqueará como si ya hubieran sido actualizadas por la transacción actual (o como si la actualización de identidad se hubiera realizado). Esto le permite actualizarlos nuevamente en la transacción actual y luego confirmar, sin que otra transacción pueda modificar estas filas de ninguna manera.Otra forma de verlo, es como si las siguientes dos declaraciones se ejecutaran de forma atómica:
Dado que las filas afectadas por
my_condition
están bloqueadas, ninguna otra transacción puede modificarlas de ninguna manera y, por lo tanto, el nivel de aislamiento de la transacción no hace ninguna diferencia aquí.Tenga en cuenta también que el nivel de aislamiento de la transacción es independiente del bloqueo: establecer un nivel de aislamiento diferente no le permite evitar el bloqueo y actualizar filas en una transacción diferente que están bloqueadas por su transacción.
Lo que los niveles de aislamiento de transacciones garantizan (en diferentes niveles) es la coherencia de los datos mientras las transacciones están en curso.
fuente
What transaction isolation levels do guarantee [...] is the consistency of data once transactions are completed.
implica incorrectamente que los niveles de aislamiento no afectan lo que sucede durante una transacción. Recomiendo revisar esta sección y proporcionar más detalles sobre cómo afectan lo que ve (o no ve) durante una transacción.