¿Cuándo usar SELECCIONAR… PARA ACTUALIZAR?

119

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 UPDATEdebe 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]
  • 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 roomshasta que el hilo 1 esté terminado. ¿Es eso correcto?

Pregunta 2 : ¿Cuándo se debe utilizar el SERIALIZABLEaislamiento de transacciones frente a READ_COMMITTEDcon 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é.

Gili
fuente
2
¿Qué RDBMS estás usando?
Quassnoi
2
@Quassnoi, como se menciona al final de la pregunta, estoy buscando una solución portátil (no específica de la base de datos).
Gili
2
¿Son las opciones REPEATABLE_READe READ_COMMITTEDincluso las opciones portátiles? Los únicos resultados que obtengo son para el servidor MSSQL
Billy ONeal
3
@BillyONeal: tenga en cuenta que los modos de aislamiento garantizan que no vea peculiaridades que no permiten, pero no diga nada sobre las peculiaridades que sí permiten. Esto significa que establecer, digamos, el READ COMMITTEDmodo no define si realmente verá o no los registros comprometidos por otra transacción: solo se asegura de que nunca verá registros no confirmados.
Quassnoi
3
Un select ... for updateon roomstodavía permitirá room_tagsque se eliminen porque son tablas separadas. ¿Quería preguntar si la for updatecláusula evitará las eliminaciones rooms?
Chris Saxon

Respuestas:

84

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 UPDATEexplícitamente.


Para resolver este problema, el hilo 1 debería SELECT id FROM rooms FOR UPDATE, evitando así que el hilo 2 se elimine roomshasta que el hilo 1 esté terminado. ¿Es eso correcto?

Esto depende del control de concurrencia que esté usando su sistema de base de datos.

  • MyISAMin MySQL(y varios otros sistemas antiguos) bloquea toda la tabla durante una consulta.

  • En SQL Server, las SELECTconsultas colocan bloqueos compartidos en los registros / páginas / tablas que han examinado, mientras que las DMLconsultas 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 que SELECTo la DELETEconsulta se bloqueará hasta que se confirme otra sesión.

  • En las bases de datos cuyo uso MVCC(como Oracle, PostgreSQL, MySQLcon InnoDB), una DMLconsulta 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 UPDATEsería útil: bloquearía SELECTo la DELETEconsulta hasta que se confirme otra sesión, al igual que lo SQL Serverhace.

¿Cuándo se debe usar el REPEATABLE_READaislamiento de transacciones en comparación READ_COMMITTEDconSELECT ... FOR UPDATE ?

Generalmente, REPEATABLE READ no prohíbe las filas fantasma (filas que aparecieron o desaparecieron en otra transacción, en lugar de ser modificadas)

  • En Oracley PostgreSQLversiones anteriores , REPEATABLE READes en realidad un sinónimo de SERIALIZABLE. 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 última Thread 1consulta 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 READy SERIALIZABLEson cosas diferentes: los lectores en SERIALIZABLEmodo establecen bloqueos de siguiente clave en los registros que evalúan, evitando de manera efectiva la concurrencia DMLen ellos. Por lo tanto, no necesita un SELECT FOR UPDATEmodo serializable, pero sí los necesita en REPEATABLE READo READ 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".

Quassnoi
fuente
1
El último punto es el quid del asunto, creo: "no necesitas un SELECT FOR UPDATE en modo serializable, pero sí los necesitas en REPEATABLE READ o READ COMMITED".
Colin 't Hart
Tienes razón. La segunda pregunta debería haber planteado cuándo SERIALIZABLEdebería usarse versus READ_COMMITTEDcon SELECT ... FOR UPDATE. ¿Puede actualizar su respuesta para reflejar esta pregunta actualizada?
Gili
1
@Gili: "no necesitas un SELECT FOR UPDATEen modo serializable", con InnoDB. Con los otros MVCCsistemas, los dos son sinónimos y los necesita SELECT FOR UPDATE.
Quassnoi
1
Creo que la publicación de Colin responde a mis preguntas específicas mejor que su respuesta, pero agradezco todas las referencias que proporcionó. Aceptaré una respuesta que combine mejor las dos (respuestas específicas arriba, referencias de apoyo abajo).
Gili
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 elimina SELECThasta el final de la transacción. Entonces, ¿no debería la respuesta simplemente Yescon las referencias de apoyo a continuación?
Gili
33

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:

select * from my_table where my_condition;

update my_table set my_column = my_column where my_condition;

Dado que las filas afectadas por my_conditionestá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.

Colin 't Hart
fuente
1
Creo que 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.
Gili
1
Encuentro que su publicación responde a mis preguntas específicas mejor que la de Quassnoi, pero agradezco todas las referencias que brindó. Aceptaré una respuesta que combine mejor las dos (respuestas específicas arriba, referencias de apoyo abajo).
Gili
El bloqueo y el aislamiento son indistintamente complicados. Entonces, ¿hay algún libro para aprender sobre eso?
Chao