2010-02-01 15 views
33

Quiero utilizar una tabla de base de datos como cola. Quiero insertarlo y tomar elementos de él en el orden insertado (FIFO). Mi principal consideración es el rendimiento porque tengo miles de estas transacciones por segundo. Así que quiero usar una consulta SQL que me brinde el primer elemento sin buscar toda la tabla. No elimino una fila cuando la leo. ¿SELECCIONA TOP 1 ..... ayuda aquí? ¿Debo usar algún índice especial?Usar una tabla de base de datos como cola

+1

Consulte este artículo para obtener una buena descripción de cómo implementar una cola en SQL Server: http://www.mssqltips.com/sqlservertip/1257/processing-data-queues-in -sql-server-with-readpast-and-updlock/ –

+0

usando una lógica basada en filas, como procesar una cola en el servidor sql es un abuso masivo de recursos ... use sql para la lógica basada en conjuntos –

Respuesta

23

Utilizaría un campo de IDENTIDAD como la clave principal para proporcionar el ID de incremento único para cada artículo en cola, y pegar un índice agrupado en él. Esto representaría el orden en que los artículos fueron puestos en cola.

Para mantener los elementos en la tabla de espera mientras los procesa, necesitaría un campo de "estado" para indicar el estado actual de un elemento en particular (por ejemplo 0 = espera, 1 = procesado, 2 = procesado) . Esto es necesario para evitar que un elemento se procese dos veces.

Al procesar elementos en la cola, deberá encontrar el siguiente elemento en la tabla que NO se esté procesando actualmente. Esto debería ser de tal forma que impida que múltiples procesos recojan el mismo elemento para procesar al mismo tiempo como se demuestra a continuación. Tenga en cuenta table hints UPDLOCK y READPAST que debe tener en cuenta al implementar colas.

p. Ej. dentro de un procedimiento almacenado, algo como esto:

DECLARE @NextID INTEGER 

BEGIN TRANSACTION 

-- Find the next queued item that is waiting to be processed 
SELECT TOP 1 @NextID = ID 
FROM MyQueueTable WITH (UPDLOCK, READPAST) 
WHERE StateField = 0 
ORDER BY ID ASC 

-- if we've found one, mark it as being processed 
IF @NextId IS NOT NULL 
    UPDATE MyQueueTable SET Status = 1 WHERE ID = @NextId 

COMMIT TRANSACTION 

-- If we've got an item from the queue, return to whatever is going to process it 
IF @NextId IS NOT NULL 
    SELECT * FROM MyQueueTable WHERE ID = @NextID 

Si el procesamiento de un elemento falla, ¿desea poder volver a intentarlo más tarde? Si es así, deberá restablecer el estado a 0 o algo así. Eso requerirá más pensamiento.

Alternativamente, no use una tabla de base de datos como cola, pero algo así como MSMQ, ¡simplemente pensé que lo lanzaría en la mezcla!

+0

¿Por qué debería separar la identificación de seleccionar *? – Shayan

+0

No es necesario, puede cargar todos los valores que necesita en las variables al mismo tiempo que el primer SELECT, y luego devolverlos al final. Además, hice "SELECCIONAR *" por simplicidad, simplemente devuelva los campos que realmente necesita. – AdaTheDev

+0

Me gustaría mantener el campo de procesos en una tabla diferente con clave externa a esta tabla para minimizar el efecto de bloqueo de diferentes partes del programa. ¿Este método ayuda? ¿Qué tipo de índice debería usar para eso? – Shayan

4

Todo depende de su motor de base de datos/implementación.

Para mí colas simples en tablas con columnas siguientes:

id/task/priority/date_added 

suele funcionar.

Utilicé las tareas de prioridad y tarea para agrupar y en el caso de la tarea duplicada elegí la que tenía mayor prioridad.

Y no se preocupe: para las bases de datos modernas "miles" no es nada especial.

+0

¿Qué es esto? Uso SQL Server 2008. – Shayan

+0

Creo que significaba "índices" en uno de los lugares donde dijo "tablas" arriba (lo arreglaría, pero no estoy * 100% * seguro de cuál es el error tipográfico). –

+0

lo siento, debería haber "columnas" – bluszcz

7

Si no elimina las filas procesadas, entonces necesitará algún tipo de indicador que indique que ya se ha procesado una fila.

Ponga un índice en esa bandera, y en la columna que va a ordenar.

Particiona tu tabla sobre esa bandera, por lo que las transacciones eliminadas no obstruyen tus consultas.

Si realmente obtienes 1.000 mensajes cada segundo, eso daría como resultado 86.400.000 filas al día. Es posible que desee pensar en alguna forma de limpiar las filas antiguas.

+0

¿Qué es una bandera? – Shayan

+0

Por 'flag' quiero decir alguna columna para recordar, si su cliente ya ha procesado una fila. –

+0

Creo que quiso decir que puede agregar una columna a sus tablas, tal vez Dequeued, que contendrá el estado de cada transacción.Como no está eliminando las filas una vez que las quita, debe tener una forma de saber qué transacciones ignorar. Puede hacer que este sea un campo de bit, con 0 para cola y 1 para cola. –

2

tal vez añadiendo un límite = 1 a la instrucción de selección ayudaría ... forzando el retorno después de un solo partido ...

+0

¿Cuál es la diferencia con TOP 1? – Shayan

+0

Sé que SQL Server puede usar el TOP 1 es lo mismo que LIMIT 1 en postgres. Me imagino que todos los demás vendedores aceptarían uno o el otro. – Matt

+1

Seré honesto, no me di cuenta de que eran equivalentes a lo mismo ... Nunca utilicé la sintaxis TOP, solo el LÍMITE ... por eso me encanta StackOverflow: incluso al proporcionar una respuesta, Aprendo algo nuevo –

2

Crear un índice agrupado en una columna de fecha (o incremento automático). Esto mantendrá las filas de la tabla aproximadamente en orden de índice y permitirá un rápido acceso basado en índice cuando ORDER BY la columna indexada. El uso de TOP X (o LIMIT X, según su RDMBS) solo recuperará los primeros x elementos del índice.

Advertencia de rendimiento: siempre debe revisar los planes de ejecución de sus consultas (en datos reales) para verificar que el optimizador no haga cosas inesperadas. También trate de comparar sus consultas (nuevamente en datos reales) para poder tomar decisiones informadas.

2

Esto no será un problema en absoluto, siempre que utilice algo para realizar un seguimiento de la fecha y hora de la inserción. Vea aquí para el mysql options. La pregunta es si solo necesita el artículo enviado más recientemente o si necesita iterar. Si necesita iterar, entonces lo que necesita hacer es tomar una porción con una instrucción ORDER BY, recorrer y recordar la última fecha y hora para que pueda usar eso cuando tome su siguiente fragmento.

2

Dado que no elimina los registros de la tabla, necesita tener un índice compuesto en (processed, id), donde processed es la columna que indica si el registro actual se ha procesado.

Lo mejor sería crear una tabla con particiones para sus registros y convertir el campo PROCESSED en la clave de partición. De esta manera, puede mantener tres o más índices locales.

Sin embargo, si siempre procesar los registros en id orden, y tienen sólo dos estados, la actualización del registro significaría simplemente tomar el registro de la primera hoja del índice y añadiendo a la última hoja

El el registro actualmente procesado siempre tendrá el menor id de todos los registros no procesados ​​y el mayor id de todos los registros procesados.

+0

Me gustaría mantener el campo de procesos en una tabla diferente con clave externa a esta tabla para minimizar el efecto de bloqueo de diferentes partes del programa. – Shayan

+4

'@ Shayan': esto tendrá un gran impacto en su rendimiento selectivo. Y necesita bloquear el campo mientras procesa de todos modos. – Quassnoi

0

Una solución muy fácil para esto para no tener transacciones, bloqueos, etc. es utilizar los mecanismos de seguimiento de cambios (no captura de datos). Utiliza el control de versiones para cada fila añadida/actualizada/eliminada para que puedas rastrear qué cambios sucedieron después de una versión específica.

Entonces, persiste la última versión y consulta los nuevos cambios.

Si una consulta falla, siempre puede volver y consultar los datos de la última versión. Además, si no desea obtener todos los cambios con una consulta, puede obtener la orden n superior por la última versión y almacenar la versión más grande que tendría que volver a consultar.

ver esto por ejemplo Using Change Tracking in SQL Server 2008

+0

¿Cómo le ayuda el cambio de seguimiento a utilizar una tabla de base de datos como cola? En una cola, desea obtener la siguiente tarea disponible (en orden FIFO) que aún no se ha procesado, y asegurarse de que el elemento solo se procese una vez. El seguimiento de cambios resuelve un problema completamente diferente: las filas de una tabla han cambiado desde la última vez que consulté. No estoy viendo la conexión. –

+0

Buen punto Brian y tú tienen razón. Propuse el seguimiento de cambios para que las colas de la tabla no se necesiten en absoluto. Ese era mi punto. En lugar de usar desencadenadores (posiblemente) u otra cosa para completar la cola, alguien podría usar los mecanismos de seguimiento de cambios para obtener los cambios directamente desde las tablas de origen, siempre y cuando quiera rastrear los cambios ... Gracias por el comentario. –

Cuestiones relacionadas