Najlepszy sposób na implementację kolejki opartej na tabeli współbieżnej

11

Mam tabelę w MySQL, która reprezentuje kolejkę linków do przetworzenia. Łącza są przetwarzane przez zewnętrzną aplikację, jedna po drugiej, i ostatecznie usuwane. Jest to kolejka o dużym wolumenie i mam wiele instancji aplikacji do przetwarzania, rozproszonych na kilku serwerach.

Jak mogę się upewnić, że każdy rekord jest wybierany tylko przez jedną aplikację? Czy istnieje sposób na oznaczenie / zablokowanie rekordu?

W tej chwili, aby uniknąć pobierania dwóch lub więcej tego samego linku, zezwalam każdemu wystąpieniu tylko na pobranie określonego zestawu rekordów (na podstawie MOD ich identyfikatora), ale nie jest to przejrzysty sposób na zwiększenie przetwarzania kolejek przyspieszyć, dodając nowe wystąpienia.

Miguel E.
źródło
Moja mantra: „Nie stój w kolejce, po prostu zrób to”. Oznacza to, że zamiast wrzucać zadanie do kolejki, uruchom proces, aby wykonać zadanie.
Rick James

Odpowiedzi:

8

Po pierwsze: MySQL jest jednym z najgorszych możliwych programów do implementacji tego, szczególnie jeśli jest bardzo dynamiczny. Powodem jest to, że silniki takie jak MEMORY i MyISAM mają tylko blokady pełnej tabeli, podczas gdy bardziej odpowiednie silniki, takie jak InnoDB, mają wyższą karę zapisu (w celu zapewnienia właściwości ACID) i są zoptymalizowane pod kątem dostępu do rekordów, które są przestrzennie i czasowo zamknięte (są ustawione na pamięć ). Nie ma też dobrego systemu powiadamiania o zmianach dla MySQL - należy go zaimplementować jako odpytywanie. Istnieją dziesiątki programów zoptymalizowanych do tego zadania .

Powiedziawszy to, widziałem z powodzeniem wdrożenie tego rodzaju dostępu, jeśli wymagania dotyczące wydajności / wydajności nie są bardzo wysokie. Wiele osób nie może sobie pozwolić na wprowadzenie i utrzymanie kompletnego oddzielnego rozwiązania technologicznego tylko dla niewielkiej części logiki biznesowej.

SELECT FOR UPDATEto jest to, czego szukasz - przeczytaj serializację. Chociaż UPDATE / DELETE zawsze blokuje wiersz podczas uruchomionej transakcji MYSQL, możesz chcieć uniknąć dużej transakcji podczas trwania procesu, więc:

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 zajmie się blokowaniem wszystkich jednoczesnych zaznaczeń oprócz jednego podczas wybierania wierszy. Ponieważ może to prowadzić do wielu zablokowanych połączeń jednocześnie, początkowa transakcja powinna być jak najmniejsza i starać się przetwarzać więcej niż 1 wiersz na raz.

jynus
źródło
Dzięki. Czy uważasz, że wydajność może przynieść większą blokadę (zmieniając LIMIT na 10)?
Miguel E,
@MiguelE Zasadniczo tak, im więcej czasu spędzasz na przetwarzaniu i im mniejsze prawdopodobieństwo kolizji z innymi transakcjami, tym lepiej. Ale w niektórych przypadkach może to zależeć - może również wywołać efekt odwrotny (zablokowanie większej liczby transakcji). Zawsze najpierw przetestuj. Ważne jest również odpowiednie indeksowanie tabeli, w przeciwnym razie możesz uzyskać blokadę pełnego stołu w niektórych trybach izolacji.
jynus,
1
I prawdopodobnie dobrym pomysłem byłoby śledzenie daty rozpoczęcia przetwarzania wiersza na wypadek, gdyby proces się zawiesił i chciałbyś wdrożyć mechanizm limitu czasu.
Julian
3

Jak wyjaśniłem w tym artykule , MySQL 8 wprowadził obsługę zarówno SKIP LOCKED, jak i NO WAIT.

POMIŃ ZABLOKOWANY jest przydatny do implementacji kolejek zadań (inaczej kolejki wsadowe), dzięki czemu można pominąć blokady, które są już zablokowane przez inne jednoczesne transakcje.

Opcja BRAK CZEKAJ jest przydatna, aby uniknąć czekania, aż równoczesna transakcja zwolni blokady, które również jesteśmy zainteresowani blokowaniem. Bez ŻADNEGO OCZEKIWANIA musimy albo poczekać, aż blokady zostaną zwolnione (w czasie zatwierdzania lub zwolnienia przez transakcję, która obecnie blokuje blokady) lub upłynie limit czasu pobierania blokady. Dlatego BRAK OCZEKIWANIA działa jak limit czasu blokady o wartości 0.

Aby uzyskać więcej informacji na temat SKIP LOCK i BEZ CZEKAJ, sprawdź ten artykuł .

Vlad Mihalcea
źródło
0

Zrobiłem coś podobnego z kontrolami DBCC offline (dwa serwery wykonują przywracanie kopii zapasowej, a następnie DBCC checkdb). Jeden serwer zbiera wczoraj wszystkie kopie zapasowe 31 serwerów i umieszcza je w kolejce, a następnie ten serwer i inny ściągają z tej kolejki. Chociaż nie jest to wiele serwerów, metoda powinna pozostać ta sama: Poproś serwer aplikacji, aby uruchomił zapytanie o aktualizację w kolejce, aktualizując pole daty / godziny i pole „serwer aplikacji” z nazwą tego serwera aplikacji lub lepszym, ale jeszcze liczbowym identyfikatorem. Spowoduje to blokadę lub jeśli już blokada z innego serwera uzyskuje następny wiersz, zostanie ona zablokowana i zaczekaj, aż inna aplikacja zakończy pobieranie następnego rzędu. Będziesz wtedy chciał, aby aplikacja pobierała najnowszy rekord z kolejki dla pola aplikacji i pobierała z niego wszelkie potrzebne informacje. Korzystanie z MySQL ”

Chris Woods
źródło