2011-03-26 35 views
18

Usando "lectura repetible" debería ser posible producir una lectura fantasma, pero ¿cómo? Lo necesito para un ejemplo de enseñanza CS-students.¿Cómo se producen las lecturas fantasmas?

Creo que debo hacer un "SELECCIONAR ... DONDE x < = 888" en un campo no indexado x, con un límite superior 888 no presente, y luego en otra conexión inserte una nueva fila con un valor solo debajo de 888.

Excepto que no funciona. ¿Necesito una mesa muy grande? ¿O algo mas?

Respuesta

12

Erik,

que vienen sólo de prueba con un gran número de filas.

Nunca encontrará fantasmas en InnoDB mysql con nivel de aislamiento de lectura comprometida o más restringido. Se explica en la documentación:

LEER REPETIBLE: Para lecturas consistentes, existe una diferencia importante con el nivel de aislamiento READ COMMITTED: Todas las lecturas consistentes dentro de la misma transacción leer la instantánea establecida por la primera lectura. Esta convención significa que si emite varias instrucciones SELECT simples (sin bloqueo) dentro de la misma transacción, estas declaraciones SELECT son consistentes también entre sí. Consulte la Sección 13.6.8.2, "Lecturas constantes sin bloqueo".

Pero tampoco puede encontrar los fantasmas en el nivel de aislamiento de lectura confirmada: Esto es necesario porque las "filas fantasmas" deben estar bloqueadas para que la replicación y la recuperación de MySQL funcionen.

información más detallada: http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html

creo que tendrá que mover a otra marca de base de datos para mostrar los fantasmas a sus estudiantes. Yo uso ambos MSSQLSERVER y Oracle.

Bueno ... es una pena para su primera pregunta.

+0

Gracias. Eso más o menos lo explica. Tendré que analizar este "problema" en uno o dos meses. Y el llamado problema solo está mostrando que puede suceder ... en otro tipo de base de datos. – Erik

+0

@deFreitas No escribí esta respuesta. Solo lo edité. Debería redirigir su comentario a danihp que escribió la respuesta. – Gili

+0

Mi error, el comentario fue dirigido a @danihp – deFreitas

0

Phantom lee puede ocurrir porque no van-cerraduras existen, a continuación, un ejemplo es (pseudocódigo):

Thread1

 
Transaction 1 

Update TableN set X=2 where X=1 

wait(s1) 
Select TableN where X=1 

Commit 

thread2

 
Transaction 2: 

insert into tableN(id, X) values(55,1) 
commit; 
notify(s1) 

en Wikipedia hay otro ejemplo de fantasma lee: Phantom Reads|wikipedia

El importante delgado g aquí está la sincronización de transacciones, puede usar puntos de sincronización.

EDITAR función Ejemplo usando mysql sueño (no probado):

 
--on thread 1 
Create TableN(id int, x int); 
insert into TableN(id, X) values(1,1); 
insert into TableN(id, X) values(2,1); 
insert into TableN(id, X) values(3,1);

BEGIN TRANSACTION; Update TableN set X=2 where X=1 SELECT SLEEP(30) FROM DUAL; select TableN from where X=1; COMMIT;

--In other thread, before 20 secs;

BEGIN TRANSACTION; insert into TableN(id, X) values(55,1);

COMMIT;

+1

Creo que el PO está buscando ** ** código real que se puede ejecutar en MySQL para demostrar esto. –

+0

Tienes razón, Martin. Sé de algunas maneras, que teóricamente puede dar una lectura fantasma, pero nunca he podido mostrarlo. Algunos de mis alumnos han intentado mucho, pero han sido en vano. – Erik

+0

Con subprocesos Java, utilizando autocommit = false y sincronización de subprocesos, puede producir esto. – CronosNull

2

Posibilidad de reproducir fantasma lee por motor InnoDB para LEER REPETIBLE nivel de aislamiento es cuestionable, debido a que InnoDB utiliza Multiversion concurrency control - para cada fila El motor de MVCC conoce los números de transacción cuando se insertó y eliminó la fila, y puede reproducir el historial de actualizaciones de filas.

Por lo tanto, todas las sentencias SELECT posteriores mostrarán el estado de la tabla al comienzo de la transacción, excepto las filas que se insertaron, borraron o actualizaron esta misma transacción. No aparecerán nuevas filas confirmadas por otras transacciones, ya que tendrán números de transacción de inserción mayores que los de esta transacción, y el rango de filas no tiene importancia aquí.

Pude reproducir PHANTOM READS para el nivel de aislamiento REPEATABLE READ para la base de datos Apache Derby, porque no usa el control de concurrencia multiversion (versión 10.8.2.2 en el momento de escribir esta respuesta).

reproducir, establecer el nivel de transacción adecuada (en ij - cliente de SQL de Derby):

-- Set autocommit off 
autocommit off; 
-- Set isolation level corresponding to ANSI REPEATABLE READ 
set isolation rs; 

T1:

SELECT * FROM TableN; 

T2:

INSERT INTO TableN VALUES(55, 1); 
COMMIT; 

T1 nuevo:

SELECT * FROM TableN; 

Ahora T1 debería ver una fila más;

+0

¡Y gracias también! Parece imposible en MySQL, por lo que podría necesitar usar Derby u otro para la demostración. – Erik

0

Para complementar la buena respuesta de Dani, podría usar Microsoft Sql Server para mostrar ese comportamiento a sus alumnos.

El servidor sql muestra las lecturas fantasmas en el nivel de aislamiento de lectura repetible según lo afirmado en la documentación here.

Postgres se suscribe a la misma noción que InnoDb como se explica here. Con Postgres también, ninguna lectura fantasma ocurre en lectura repetible y, por lo tanto, tampoco es adecuada para su propósito didáctico.

Sql Server ofrece otro nivel de aislamiento, instantánea, que hace lo que MySql InnoDb y Postgres hacen en lectura repetible (que es una implementación sin bloqueos, basada en versiones de lectura repetible sin lecturas fantasmas, pero no serializable).

Sql Server Express es gratuito aunque necesita una máquina con Windows. También puede obtener una cuenta de Windows Azure y mostrar ese comportamiento con Sql Azure en línea.

0

La "lectura fantasma" en el nivel de aislamiento de MySQL en RR está oculta profundamente, pero todavía puede reproducirla. Estos son los pasos:

  1. crear tabla ab (una clave principal int, b int);

  2. Tx1:
    begin;
    select * from ab; // juego vacío

  3. Tx2:
    comenzar;
    insertar en valores ab (1,1);
    commit;
  4. Tx1:
    seleccionar * de ab; // set vacío, lectura fantasma esperado faltante.
    update ab set b = 2 donde a = 1; // 1 fila afectada.
    select * from ab; // 1 fila. phantom leer aquí !!!!
    commit;
1

InnoDB debe proteger contra lecturas fantasmas, como otros han escrito.

Pero InnoDB tiene un comportamiento extraño diferente relacionado con el bloqueo. Cuando una consulta adquiere un bloqueo, siempre adquiere el bloqueo en la versión más reciente de la fila. A fin de tratar el siguiente

CREATE TABLE foo (i INT PRIMARY KEY, val INT); 
INSERT INTO foo (i, val) VALUES (1, 10), (2, 20), (3, 30); 

Luego, en dos sesiones simultáneas (abrir dos ventanas de terminal):

-- window 1        -- window 2 
START TRANSACTION; 
              START TRANSACTION; 

              SELECT * FROM foo; 

UPDATE foo SET val=35 WHERE i=3; 

              SELECT * FROM foo; 

Esto debería mostrar val = 10, 20, 30 en ambos SELECTs, ya REPETIBLE-LEER significa la La segunda ventana solo ve los datos tal como existían cuando comenzó su transacción.

Sin embargo:

          SELECT * FROM foo FOR UPDATE; 

La segunda ventana de espera para adquirir el bloqueo en fila 3.

COMMIT; 

Ahora el SELECT en el segundo acabados de ventana, y muestra las filas con val = 10, 20 , 35, porque el bloqueo de la fila hace que SELECT vea la versión confirmada más reciente. Las operaciones de bloqueo en InnoDB actúan como si se ejecutaran bajo READ-COMMITTED, independientemente del nivel de aislamiento de la transacción.

Incluso puede alternar:

          SELECT * FROM foo; 

              SELECT * FROM foo FOR UPDATE; 

              SELECT * FROM foo; 

              SELECT * FROM foo FOR UPDATE; 
Cuestiones relacionadas