2012-06-07 12 views
77

Por favor, ayúdenme a entender el caso de uso detrás de SELECT ... FOR UPDATE.Cuándo usar SELECCIONAR ... PARA ACTUALIZAR?

Pregunta 1: ¿Es el siguiente un buen ejemplo de cuándo se debe usar SELECT ... FOR UPDATE?

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 entre las habitaciones sin etiquetas y las habitaciones que se han eliminado. Si SELECT ... FOR no se utiliza UPDATE, 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]
  • Tema 2: DELETE FROM room_tags WHERE room_id = 1;
  • Tema 2: DELETE FROM rooms WHERE id = 1;
  • Tema 2: [confirma la transacción]
  • Tema 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 subproceso 1 cree que la sala 1 no tiene etiquetas, pero en realidad la sala se ha eliminado. Para resolver este problema, el subproceso 1 debe SELECT id FROM rooms FOR UPDATE, evitando así que el subproceso 2 elimine desde rooms hasta que finalice el subproceso 1. ¿Es eso correcto?

Pregunta 2: ¿Cuándo se debe utilizar SERIALIZABLE aislamiento de transacción en comparación con READ_COMMITTEDSELECT ... FOR UPDATE?

Se espera que las respuestas sean portátiles (no específicas de la base de datos). Si eso no es posible, explica por qué.

+2

¿Qué RDBMS estás usando? – Quassnoi

+0

@Quassnoi, como se menciona en la parte inferior de la pregunta, estoy buscando una solución portátil (no específica de la base de datos). – Gili

+1

¿Las opciones 'REPEATABLE_READ' y' READ_COMMITTED' son incluso opciones portátiles? Los únicos resultados que obtengo para aquellos son para el servidor MSSQL –

Respuesta

57

La única forma portátil de lograr coherencia entre habitaciones y etiquetas y asegurarse de que las salas nunca se devuelvan después de haber sido eliminadas las bloquea con SELECT FOR UPDATE.

Sin embargo, en algunos sistemas, el bloqueo es un efecto secundario del control de concurrencia, y se obtienen los mismos resultados sin especificar FOR UPDATE explícitamente.


Para resolver este problema, debe Tema 1 SELECT id FROM rooms FOR UPDATE, evitando así Tema 2 de suprimir de rooms hasta Tema 1 está hecho. ¿Es eso correcto?

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

  • MyISAM en MySQL (y varios otros sistemas antiguos) ¿Bloquea toda la mesa para la duración de una consulta.

  • En SQL Server, SELECT consultas lugar bloqueos compartidos en los registros/páginas/cuadros que han examinados, mientras que las cerraduras DML consultas lugar de actualización (que más tarde ascendidas a exclusiva o degradados a los bloqueos compartidos). Los bloqueos exclusivos son incompatibles con los bloqueos compartidos, por lo tanto, la consulta SELECT o DELETE se bloqueará hasta que se confirme otra sesión.

  • En las bases de datos que utilizan MVCC (como Oracle, PostgreSQL, MySQL con InnoDB), una consulta DML 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, un SELECT FOR UPDATE sería útil: bloquearía SELECT o la consulta DELETE hasta que otra sesión se comprometa, como lo hace SQL Server.

¿Cuándo se debe utilizar REPEATABLE_READ aislamiento de transacción en comparación con READ_COMMITTEDSELECT ... FOR UPDATE?

Generalmente, REPEATABLE READ no prohíbe filas fantasma (filas que aparecían o desaparecieron en otra transacción, en lugar de ser modificado)

  • En Oracle y anteriores PostgreSQL versiones, REPEATABLE READ es en realidad un sinónimo de SERIALIZABLE . Básicamente, esto significa que la transacción no ve los cambios realizados después de que haya comenzado. Por lo tanto, en esta configuración, la última consulta Thread 1 devolverá la sala como si nunca se hubiera eliminado (que puede ser o no lo que usted quería). Si no desea mostrar las habitaciones después de que hayan sido eliminados, debe bloquear las filas con SELECT FOR UPDATE

  • En InnoDB, REPEATABLE READ y SERIALIZABLE son cosas diferentes: los lectores en SERIALIZABLE modo conjunto Cierres de próxima clave en los registros evalúan, evitando de manera efectiva el DML simultáneo en ellos. Por lo tanto, no necesita un SELECT FOR UPDATE en modo serializable, pero sí los necesita en REPEATABLE READ o READ COMMITED.

Tenga en cuenta que la norma sobre modos de aislamiento le prescribe que no se ve ciertas peculiaridades en sus consultas, pero no define cómo (con el bloqueo o con MVCC o de otra manera).

Cuando digo "no necesita SELECT FOR UPDATE" Realmente debería haber agregado "debido a los efectos secundarios de la implementación de ciertos motores de base de datos".

+1

El último punto es el quid de la cuestión, creo: "no necesita un SELECCIONAR PARA LA ACTUALIZACIÓN en modo serializable, pero sí los necesita en REPEATABLE READ o READ COMMITHED". –

+0

Tienes razón. La segunda pregunta debería haber preguntado cuándo se debe usar 'SERIALIZABLE' contra' READ_COMMITTED' con 'SELECT ... FOR UPDATE'. ¿Puedes actualizar tu respuesta para reflejar esta pregunta actualizada? – Gili

+0

@Gili: "no necesita un' SELECT FOR UPDATE' en modo serializable ", con' InnoDB'. Con los otros sistemas 'MVCC', los dos son sinónimos y usted necesita' SELECT FOR UPDATE'. – Quassnoi

14

Respuestas cortas:

Q1: Sí.

Q2: No importa cuál use.

Respuesta larga:

Un select ... for update será (ya que implica) seleccionar algunas filas, sino también encerrarlos como si ya se han actualizado por la transacción actual (o como si la actualización de la identidad se había 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 ver las cosas, es como si las dos afirmaciones siguientes se ejecutan atómicamente:

select * from my_table where my_condition; 

update my_table set my_column = my_column where my_condition; 

Desde las filas afectadas por my_condition están bloqueadas, ninguna otra transacción puede modificar de ninguna manera, y por lo tanto, el nivel de aislamiento de transacción no hace diferencia aquí.

Tenga en cuenta también que el nivel de aislamiento de transacción es independiente del bloqueo: la configuración de un nivel de aislamiento diferente no le permite bloquear y actualizar filas en una transacción diferente bloqueada por su transacción.

Lo que los niveles de aislamiento de transacción garantizan (en diferentes niveles) es la coherencia de los datos mientras las transacciones están en progreso.

+1

Pienso 'Lo que los niveles de aislamiento de la transacción garantizan [...] es la consistencia de los datos una vez que se completan las transacciones. Incorrectamente implica 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 ves (o no ves) durante una transacción. – Gili

+0

Me parece que su publicación responde a mis preguntas específicas mejor que [Quassnoi's] (http://stackoverflow.com/a/16425603/14731) pero aprecio todas las referencias que proporcionó. Aceptaré una respuesta que combine mejor las dos (respuestas específicas en la parte superior, referencias de apoyo a continuación). – Gili

+0

El bloqueo y el aislamiento son intercambiablemente complicados. Entonces, ¿hay libros para obtener el conocimiento al respecto? – Chao

Cuestiones relacionadas