La mejor manera de implementar la cola basada en tablas concurrentes


10

Tengo una tabla en MySQL que representa una cola de enlaces para procesar. Los enlaces son procesados ​​por una aplicación externa, uno por uno, y eliminados al final. Esta es una cola de alto volumen y tengo varias instancias de la aplicación de procesamiento, repartidas en varios servidores.

¿Cómo puedo asegurarme de que cada registro sea seleccionado por una sola aplicación? ¿Hay alguna manera de marcar / bloquear el registro?

En este momento, para evitar que dos o más tomen el mismo enlace, estoy permitiendo que cada instancia solo tome un cierto conjunto de registros (basado en el MOD de su ID), pero esta no es una forma transparente de aumentar el procesamiento de la cola acelerar simplemente agregando nuevas instancias.


Mi mantra: "No hagas cola, solo hazlo". Es decir, en lugar de lanzar una tarea a una cola, inicie un proceso para realizar la tarea.
Rick James

Respuestas:


7

Primero: MySQL es una de las peores piezas de software posibles para implementar esto, especialmente si es muy dinámico. La razón es que los motores como MEMORY y MyISAM solo tienen bloqueos de tabla completa, mientras que los motores más adecuados como InnoDB tienen una penalización de escritura más alta (para proporcionar propiedades ACID) y están optimizados para acceder a registros que están cercanos espacial y temporalmente (se configuran en la memoria ) Tampoco existe un buen sistema de notificación de cambios para MySQL; debe implementarse como una encuesta. Hay docenas de piezas de software más optimizadas para esa tarea .

Dicho esto, he visto implementar con éxito este tipo de acceso si los requisitos de rendimiento / eficiencia no son muy altos. Muchas personas no pueden permitirse el lujo de introducir y mantener una pieza de tecnología completamente separada solo por una pequeña parte de la lógica empresarial.

SELECT FOR UPDATEes lo que está buscando: lea la serialización. Si bien una ACTUALIZACIÓN / BORRAR siempre bloqueará la fila durante una transacción MYSQL en ejecución, es posible que desee evitar una transacción grande mientras el proceso continúa, por lo tanto:

START TRANSACTION;
SELECT * FROM your_table WHERE state != 'PROCESSING' 
  ORDER BY date_added ASC LIMIT 1 FOR UPDATE;
if (rows_selected = 0) { //finished processing the queue, abort}
else {
UPDATE your_table WHERE id = $row.id SET state = 'PROCESSING'
COMMIT;

// row is processed here, outside of the transaction, and it can take as much time as we want

// once we finish:
DELETE FROM your_table WHERE id = $row.id and state = 'PROCESSING' LIMIT 1;
}

MySQL se encargará de bloquear todas las selecciones simultáneas excepto una al seleccionar filas. Como esto puede conducir a muchas conexiones bloqueadas al mismo tiempo, mantenga la transacción inicial lo más pequeña posible e intente procesar más de 1 fila a la vez.


Gracias. ¿Crees que el rendimiento puede beneficiarse de un bloqueo más grande (al cambiar el LÍMITE para decir 10)?
Miguel E

@MiguelE En general, sí, cuanto más tiempo pase procesando y menos probable sea que choque con otras transacciones, mejor. Pero puede depender en algunos casos, también podría causar el efecto contrario (se bloquean más transacciones). Siempre pruébalo primero. También es importante indexar adecuadamente la tabla, o puede terminar con un bloqueo completo de la tabla en algunos modos de aislamiento.
jynus

1
Y probablemente sería una buena idea hacer un seguimiento de la fecha en que comenzó a procesar la fila en caso de que el proceso se bloquee y desee implementar un mecanismo de tiempo de espera.
Julian

3

Como expliqué en este artículo , MySQL 8 introdujo soporte para SKIP LOCKED y NO WAIT.

SKIP LOCKED es útil para implementar colas de trabajos (también conocidas como colas por lotes) para que pueda saltarse los bloqueos que ya están bloqueados por otras transacciones simultáneas.

NO WAIT es útil para evitar esperar hasta que una transacción concurrente libere los bloqueos que también estamos interesados ​​en bloquear. Sin NO WAIT, tenemos que esperar hasta que se liberen los bloqueos (en el momento de confirmación o liberación por la transacción que actualmente mantiene los bloqueos) o el tiempo de espera de la adquisición del bloqueo. Por lo tanto, NO WAIT actúa como un tiempo de espera de bloqueo con un valor de 0.

Para obtener más detalles sobre SKIP LOCK y NO WAIT, consulte este artículo .


0

He hecho algo similar con las comprobaciones de DBCC sin conexión (dos servidores que realizan restauraciones de copia de seguridad y luego un DBCC checkdb). Un servidor reúne todos los 31 respaldos del servidor ayer y los pone en una cola y luego ese servidor y otro extraen de esa cola. Si bien no hay muchos servidores, el método debe seguir siendo el mismo: haga que el servidor de aplicaciones ejecute una consulta de actualización en la cola actualizando un campo de fecha / hora y un campo de "servidor de aplicaciones" con el nombre de ese servidor de aplicaciones o, mejor aún, la identificación numérica. Esto provocará un bloqueo o si ya hay un bloqueo de otro servidor que obtiene la siguiente fila, se bloqueará y esperará a que la otra aplicación termine de obtener la siguiente fila. Luego, querrá que la aplicación retire el registro más reciente de la cola para su campo de aplicación y obtenga la información que desee. Usando MySQL '

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.