Il modo migliore per implementare code basate su tabelle simultanee


10

Ho una tabella in MySQL che rappresenta una coda di collegamenti da elaborare. I collegamenti vengono elaborati da un'app esterna, uno per uno, e infine eliminati. Questa è una coda ad alto volume e ho più istanze dell'app di elaborazione, distribuite su più server.

Come posso assicurarmi che ogni record sia selezionato da una sola app? C'è un modo per contrassegnare / bloccare il record?

In questo momento, per evitare che due o più raccolgano lo stesso collegamento, consento a ciascuna istanza di raccogliere solo un determinato set di record (basato sulla MOD del loro ID), ma questo non è un modo trasparente per aumentare l'elaborazione della coda velocità semplicemente aggiungendo nuove istanze.


Il mio mantra: "Non metterlo in coda, fallo e basta". Cioè, invece di lanciare un'attività in una coda, avviare un processo per eseguire l'attività.
Rick James,

Risposte:


7

Primo: MySQL è uno dei peggiori software possibili per implementarlo, specialmente se è molto dinamico. Il motivo è che motori come MEMORY e MyISAM hanno solo blocchi a tabella intera mentre i motori più adatti come InnoDB hanno una penalità di scrittura più elevata (per fornire proprietà ACID) e sono ottimizzati per l'accesso a record che sono spazialmente e temporalmente vicini (quelli sono impostati in memoria ). Inoltre, non esiste un buon sistema di notifica delle modifiche per MySQL, che deve essere implementato come polling. Esistono dozzine di software più ottimizzati per tale compito .

Detto questo, ho visto implementare con successo questo tipo di accesso se i requisiti di prestazioni / efficienza non sono molto elevati. Molte persone non possono permettersi di introdurre e mantenere una tecnologia completamente separata solo per una piccola parte della logica aziendale.

SELECT FOR UPDATEè quello che stai cercando, leggi la serializzazione. Mentre un UPDATE / DELETE bloccherà sempre la riga durante una transazione MYSQL in esecuzione, potresti voler evitare una transazione di grandi dimensioni mentre il processo è in corso, quindi:

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 si occuperà di bloccare tutte le selezioni simultanee tranne una durante la selezione delle righe. Poiché ciò può portare a molte connessioni bloccate contemporaneamente, mantenere la transazione iniziale il più piccola possibile e provare a elaborare più di 1 riga alla volta.


Grazie. Pensi che le prestazioni possano beneficiare di un blocco più grande (modificando il LIMIT per dire 10)?
Miguel E

@MiguelE In generale, sì, maggiore è il tempo impiegato per l'elaborazione e minore è la probabilità di scontrarsi con altre transazioni, meglio è. Ma può dipendere in alcuni casi, potrebbe anche causare l'effetto opposto (più transazioni bloccate). Provalo sempre prima. È anche importante indicizzare adeguatamente la tabella, altrimenti potresti finire con un blocco completo della tabella in alcune modalità di isolamento.
jynus,

1
E sarebbe probabilmente una buona idea tenere traccia della data in cui hai iniziato a elaborare la riga nel caso in cui il processo si blocchi e desideri implementare un meccanismo di timeout.
Giuliano,

3

Come ho spiegato in questo articolo , MySQL 8 ha introdotto il supporto per SKIP LOCKED e NO WAIT.

SKIP LOCKED è utile per l'implementazione di code di lavoro (ovvero code batch) in modo da poter saltare i blocchi già bloccati da altre transazioni simultanee.

NO WAIT è utile per evitare l'attesa fino a quando una transazione simultanea rilascia i blocchi che ci interessa anche bloccare. Senza NESSUNA ATTESA, dobbiamo attendere il rilascio dei blocchi (al momento del commit o del rilascio da parte della transazione che attualmente detiene i blocchi) o il timeout dell'acquisizione dei blocchi. Pertanto, NO WAIT si comporta come un timeout di blocco con un valore di 0.

Per maggiori dettagli su SKIP LOCK e NO WAIT, leggi questo articolo .


0

Ho fatto qualcosa di simile con i controlli DBCC offline (due server che eseguono ripristini di backup e quindi un controllo DBCC). Un server raccoglie tutti i 31 backup del server ieri e li inserisce in una coda, quindi quel server e un altro pull da quella coda. Sebbene non ci siano molti server, il metodo dovrebbe rimanere lo stesso: fare in modo che il server delle applicazioni esegua una query di aggiornamento sulla coda aggiornando un campo data / ora e un campo "server app" con il nome di quel server app o un ID numerico migliore. Ciò causerà un blocco o se esiste già un blocco da un altro server che ottiene la riga successiva, verrà bloccato e attenderà che l'altra app finisca di ottenere la riga successiva. Sarà quindi necessario che l'app recuperi il record più recente dalla coda per il suo campo dell'app e ottenga da esso tutte le informazioni desiderate. Utilizzando MySQL '

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.