2010-09-21 14 views
5

Załóżmy, że mam tabelę bazy danych SQL Server z X rekordami (> 1 000 000), które muszą zostać przetworzone (uzyskać dane, wykonać działanie zewnętrzne, stan aktualizacji w db) jeden po drugim przez niektóre procesy robocze (albo aplikacje konsolowe, usługi Windows, role robotów Azure itp.). Muszę zagwarantować, że każdy wiersz jest przetwarzany tylko raz. Idealnie zagwarantowana byłaby wyłączność, niezależnie od tego, ile maszyn/procesów zostało przetworzonych w celu przetworzenia wiadomości. Martwię się głównie o dwa WYBIERAJĄCE chwytające te same rzędy jednocześnie.Jak zaprojektowałbyś ten system przetwarzania komunikatów w .NET/SQL Server?

Wiem, że istnieją lepsze bazy danych do kolejkowania, ale nie mam tego luksusu dla tego projektu. Mam pomysły na osiągnięcie tego, ale szukam czegoś więcej.

Odpowiedz

7

Miałem tę sytuację.

InProcess Dodaj kolumnę do tabeli, domyślnie = 0. W procesie konsumentów:

UPDATE tbl SET Inprocess = @myMachineID WHERE rowID = 
    (SELECT MIN(rowID) WHERE InProcess = 0) 

Teraz, gdy maszyna posiada wiersz, a można wyszukać swoje dane bez strachu. Zwykle jest to następna linia będzie coś takiego:

SELECT * FROM tbl WHERE rowID = 
    (SELECT MAX(rowID) FROM tbl WHERE ProcessID = @myMachineID) 

Będziesz mieć również dodać Done flagę jakiegoś do rzędu, więc można powiedzieć, jeśli wiersz twierdzono, ale przetwarzanie był niekompletny.

Edit

UPDATE dostaje wyłączną blokadę (patrz MSDN). Nie jestem pewien, czy SELECT w podzapytaniu może być podzielony z UPDATE; jeśli tak, musiałbyś umieścić je w transakcji.

@Will A posty link, który sugeruje, że zaczynając swoją partię z tym będzie to gwarantuje:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 

... ale nie próbowałem go.

@ link do Martina Smitha również robi kilka dobrych rzeczy, patrząc na klauzulę OUTPUT (dodaną w SQL 2005).

ostatnia edycja

Bardzo interesująca wymiana w komentarzach, na pewno dowiedział się tu kilka rzeczy. I po to jest SO, prawda?

Tylko dla koloru: kiedy użyłem tego podejścia w 2004 r., Miałem kilka robotów sieciowych, które pobierały adresy URL do przeszukania w tabeli, a następnie wyciągały kolejny URL do indeksowania z tej samej tabeli. Ponieważ roboty próbowały przyciągnąć złośliwe oprogramowanie, w każdej chwili mogły ulec awarii.

+0

+1 Potrzebne jest pozapasmowe porządki w przypadku, gdy aplikacja konsumencka nie przechodzi poprawnie między "InProcess" i "Gotowe" –

+0

Dowolny powód, dla którego @myMachineID nie może być po prostu @@ SPID, oczywiście przy założeniu, że że oba zapytania są wykonywane w tej samej partii? –

+0

Czy AKTUALIZACJA blokuje wybrane wiersze podczas ich aktualizowania lub czy wiele procesów może jednocześnie odbierać wiersze? –

0

Uznalbym, że proces pobiera najwyższą liczbę N rekordów, których "przetworzona" flaga ma wartość zero w lokalnej kolekcji. Rzeczywiście miałbym trzy wartości dla przetworzonej flagi: NotProcessed (0), Processing (2), Processsed (1). Następnie pętli swojej kolekcji i wydać następujące sql:

update table_of_records_to_process 
set processed = 2 
where record_id = 123456 
and processed = 0 

... w ten sposób, jeśli jakiś inny proces ten już chwycił ID rekordu, to nie będzie w polu przetwarzane w celu 2.Musisz sprawdzić, czy identyfikator rekordu 123456 jest naprawdę ustawiony na 2:

select count(*) 
from table_of_records_to_process 
where record_id = 123456 
and processed = 2 

... możesz go przetworzyć. Jeśli liczba zwrócona wynosi zero, przejdziesz do następnego rekordu w swojej kolekcji i spróbuj ponownie. Jeśli dojdziesz do końca kolekcji i jakiś inny proces już zmodyfikował wszystkie te rekordy, idź po N więcej rekordów.

Powiązane problemy