2011-08-03 15 views
5

Estoy recopilando lecturas de varios miles de sensores y almacenándolos en una base de datos MySQL. Hay varios cientos de insertos por segundo. Para mejorar el rendimiento de inserción, estoy almacenando los valores inicialmente en una tabla de memoria intermedia MEMORY. Una vez por minuto, ejecuto un procedimiento almacenado que mueve las filas insertadas desde el búfer de memoria a una tabla permanente.¿Cómo mover atómicamente las filas de una tabla a otra?

Básicamente me gustaría hacer lo siguiente en mi procedimiento almacenado para mover las filas de la memoria intermedia temporal:

INSERT INTO data SELECT * FROM data_buffer; 
DELETE FROM data_buffer; 

Por desgracia, la anterior no es utilizable debido a que los procesos de recopilación de datos insertar filas adicionales en "data_buffer" entre INSERTAR y ELIMINAR arriba. Por lo tanto, esas filas se eliminarán sin ser insertadas en la tabla de "datos".

¿Cómo puedo hacer que la operación sea atómica o hacer que la instrucción DELETE elimine solo las filas que fueron SELECCIONADAS e INSERTADAS en la declaración anterior?

Preferiría hacer esto de una manera estándar que funciona en diferentes motores de base de datos si es posible.

Preferiría no agregar ninguna columna "id" adicional debido a los gastos generales de rendimiento y los requisitos de almacenamiento.

Me gustaría que hubiera SELECT_AND_DELETE o la declaración de MOVE en SQL estándar o algo similar ...

+0

¿Podría proporcionar la estructura de la tabla data_buffer? –

+0

Sure: CREATE TABLE 'data_buffer' ( ' int tiempo' (11) NOT NULL, 'sensor' smallint (6) NOT NULL, ' valor' flotar NOT NULL ) ENGINE = memoria predeterminada charset = latin1; – snap

+0

Tengo una solución específica de MySQL, pero parece que no puedo publicarla en la sección de respuestas antes de que hayan transcurrido 8 horas. Realmente odio estos límites en stackoverflow ... – snap

Respuesta

1

Una posible forma de evitar todos esos problemas, y también mantenerse rápido, sería usar dos tablas data_buffer (llamémoslas data_buffer1 y data_buffer2); mientras que los procesos de recopilación se insertan en data_buffer2, puede hacer el insert y delete en data_buffer2; de lo que cambia, los datos recopilados van al data_buffer2, mientras que los datos se insertan + eliminan del data_buffer1 al data.

+0

Al final implementé una variación de esta solución: la solución RENAME TABLE sobre la que escribí en mi propia respuesta. Sin embargo, debido a que se basa en la idea de esta respuesta, elijo esta respuesta como mi respuesta aceptada. El uso de tablas alternas es la mejor solución en mi caso, ya que no hay sobrecarga adicional de las columnas de identificación de mantenimiento de registros y las inserciones de los sensores no se bloquearán debido al bloqueo. – snap

3

que creo que esto funcionará, pero se bloqueará hasta inserción se realiza

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 
BEGIN TRANSACTION; 
INSERT INTO data (SELECT * FROM data_buffer FOR UPDATE); 
DELETE FROM data_buffer; 
COMMIT TRANSACTION; 
+0

No. "SELECCIONAR PARA ACTUALIZAR" bloquea solo las filas SELECCIONADAS pero no impide INSERTAR filas nuevas. – snap

+0

He editado, .. espero que esto ayude –

+0

Lo siento. En realidad, funciona con tablas de MEMORIA (si se hacen en una sola transacción) porque actualmente solo admiten bloqueos a nivel de tabla. Prefiero no depender de las características actuales del motor de almacenamiento, quién sabe si la próxima versión de MySQL implementa bloqueos a nivel de fila para tablas de memoria o si alguien decide colocar la tabla temporal en una tabla de memoria NDB (que admite bloqueos de nivel de fila). – snap

0

Asumo las tablas son idénticos , con las mismas columnas y tecla (s) primaria (s)? Si ese es el caso, se podría seleccionar Ubicado dentro de una cláusula where ... algo como esto:

DELETE FROM data_buffer 
WHERE primarykey IN (SELECT primarykey FROM data) 
+0

Esto funcionaría si hubiera alguna "clave primaria". Actualmente no existe porque no es necesario para otros fines. Sin embargo, creo que sería muy lento porque hay una ** enorme ** cantidad de filas en la tabla * data *. – snap

1

¿Qué hay de tener un identificador de fila, obtener el valor máximo antes de insertar, hacer la inserción y luego eliminar registros < = max (id)

1

Esta es una solución similar a la respuesta de @ammoQ. La diferencia es que en lugar de que el proceso de INSERTing determine en qué tabla escribir, puede intercambiar las tablas de manera transparente en el procedimiento programado.

Uso cambie el nombre en el procedimiento programado para intercambiar tablas:

CREATE TABLE IF NOT EXISTS data_buffer_new LIKE data_buffer; 
RENAME TABLE data_buffer TO data_buffer_old, data_buffer_new TO data_buffer; 
INSERT INTO data SELECT * FROM data_buffer_old; 
DROP TABLE data_buffer_old; 

Esto funciona porque sentencia RENAME cambia las tablas de forma atómica, por lo tanto los procesos de inserción de no fallar con "mesa no encontrado". Sin embargo, esto es específico de MySQL.

0

Esta es una solución específica de MySQL. Puede usar el bloqueo para evitar que los procesos INSERTing agreguen nuevas filas mientras mueve las filas.

El procedimiento que mueve las filas deben ser como sigue:

LOCK TABLE data_buffer READ; 
INSERT INTO data SELECT * FROM data_buffer; 
DELETE FROM data_buffer; 
UNLOCK TABLE; 

El código que inserta nuevas filas en la memoria intermedia se deben cambiar de la siguiente manera:

LOCK TABLE data_buffer WRITE; 
INSERT INTO data_buffer VALUES (1, 2, 3); 
UNLOCK TABLE; 

El proceso de inserción de bloque, obviamente, mientras la cerradura está en su lugar.

+2

Parece que esta solución es incorrecta, porque no es posible usar LOCK TABLES en procedimientos almacenados como se describe en (http://dev.mysql.com/doc/refman/5.1/en/stored-program-restrictions.html) Desafortunadamente no puedo rechazar mi propia respuesta. :) – snap

Cuestiones relacionadas