2009-05-20 11 views
5

Mam tabelę w bazie danych SQL Server 2005, który jest często używany. Ma nasz produkt pod ręką informacje o dostępności. Co godzinę otrzymujemy aktualizacje z naszego magazynu i od kilku lat uruchamiamy procedurę, która obcina tabelę i aktualizuje informacje. Zajmie to tylko kilka sekund i do tej pory nie stanowiło problemu. Mamy znacznie więcej osób korzystających z naszych systemów, które teraz przesyłają zapytania do tych informacji, w wyniku czego widzimy wiele przekroczeń czasu z powodu blokowania procesów.Jaki jest najlepszy sposób aktualizowania danych w tabeli, gdy jest ona używana bez blokowania tabeli?

... więc ...

Zbadaliśmy nasze opcje i mają pochodzić z pomysłem, aby złagodzić ten problem.

  1. Mielibyśmy dwie tabele. Tabela A (aktywna) i tabela B (nieaktywna).
  2. Stworzyliśmy widok wskazujący na aktywną tabelę (tabela A).
  3. Wszystkie rzeczy, które potrzebują tej informacji w tabelach (4 obiekty), będą teraz musiały przejść przez widok.
  4. Procedura godzinowa obcięłaby nieaktywną tabelę, zaktualizowała ją o najnowsze informacje, a następnie zaktualizowała widok, aby wskazywała na nieaktywną tabelę, czyniąc ją aktywną.
  5. Ta procedura określa, która tabela jest aktywna i zasadniczo przełącza widok między nimi.

Co jest nie tak z tym? Czy przełączenie średniego zapytania widoku spowoduje problemy? Czy to działa?

Dziękujemy za Twoje doświadczenie.

dodatkowych informacji

  • procedura jest pakiet SSIS że peforms wiele kroków i ostatecznie obcina/aktualizuje tabelę w pytaniu

  • Procesy blokujące dwa inne procedury składowane tego zapytania to stół.

+0

jeśli masz licencji, dwa oddzielne serwery symetryczne obciążenie mogłoby zapewnić bezproblemowe alternatywy. Trzymasz jeden na żywo i aktualizujesz drugi, a następnie przełączasz się. –

Odpowiedz

6

Czy rozważałeś użycie snapshot isolation. Umożliwiłoby to rozpoczęcie wielkiej, grubej transakcji na rzecz SSIS i kontynuowanie czytania ze stołu.

To rozwiązanie wydaje się znacznie czystsze niż przełączanie tabel.

+0

+1 w tej sprawie. Dokładnie to powinieneś robić. Najlepiej byłoby, gdybyś otrzymał aktualizacje, które nie wymagałyby zrzucenia wszystkiego, ale ponieważ tak się nie stało, wystarczy uruchomić transakcję, usunąć (nie skrócić) wszystko, a następnie wczytać wszystko. W momencie uruchomienia commit, baza danych sprawi, że wszystko się zmieni bez luki. –

+0

Dlaczego nie przyciąć? Czy nie byłoby wolniej usuwać, ponieważ każde usunięcie jest rejestrowane? –

+0

Myślę, że o to chodzi. używając izolacji migawki, jej zatwierdzenie, które powoduje, że użytkownicy widzą nowe dane. truncate nie jest rejestrowane i nie wiem jak wpłynęłoby to na transakcję, ale prawdopodobnie nie, jak byś chciał. W rzeczywistości założę się, że obcięcie zakończyłoby się niepowodzeniem, gdyby tabela miała oczekującą transakcję. – Zack

2

Myślę, że dzieje się to w niewłaściwy sposób - aktualizacja tabeli musi ją zablokować, chociaż można ograniczyć to blokowanie do strony, a nawet do wiersza.

Spoglądam na nie ścinając stołu i uzupełniając go. To zawsze będzie przeszkadzać użytkownikom próbującym ją przeczytać.

Jeśli zaktualizowałeś zamiast wymienić tabelę, mógłbyś to kontrolować w inny sposób - czytelnicy nie powinni blokować tabeli i mogą odejść z optymistycznymi odczytami.

Spróbuj dodać podpowiedź z (nolock) do czytania instrukcji SQL View. Powinieneś być w stanie uzyskać bardzo duże ilości czytelników, nawet jeśli tabela jest regularnie aktualizowana.

+0

Rozumiem, że podpowiedzi SQl, szczególnie NOLOCK, są złe z wielu powodów. http://tinyurl.com/qwloxh –

+1

Nie są one tak blisko "złe", jak tworzenie kopii danych i przebudowywanie widoków w celu uniknięcia blokowania. Najlepszym sposobem jest ustawienie blokady we właściwościach transakcji, ale nie jest to możliwe, ponieważ używasz obcięcia. Również nie zgadzam się z punktami zawartymi w tym artykule - czasami wskazówki SQL są najlepszym sposobem radzenia sobie z zakleszczeniami, ale musisz zrozumieć, co robisz z nimi. – Keith

+0

Aha, a SO robi to: http://www.codinghorror.com/blog/archives/001166.html – Keith

1

Dlaczego nie używać transakcji do aktualizacji informacji, a nie operacji skracania.

Funkcja obcięcia nie jest rejestrowana, więc nie można jej wykonać w transakcji.

Jeśli operacja jest przeprowadzana w transakcji, nie wpłynie to na istniejących użytkowników.

Sposób wykonania tej czynności zależy od wielkości tabeli i od tego, jak radykalnie zmieniają się dane. Jeśli podasz więcej szczegółów, mógłbym ci doradzić dalej.

+0

Procedura jest pakietem SSIS i wygląda na to, że cała sprawa działa w transakcji, co powoduje blokowanie. –

2

Osobiście, jeśli zawsze zamierzasz wprowadzić czas przestoju, aby uruchomić proces wsadowy względem tabeli, myślę, że powinieneś zarządzać doświadczeniami użytkownika w warstwie dostępu do biznesu/danych. Przedstaw obiekt zarządzania tabelą, który monitoruje połączenia z tą tabelą i kontroluje przetwarzanie wsadowe.

Gdy nowe dane wsadowe są gotowe, obiekt zarządzania zatrzymuje wszystkie nowe zapytania kwerendy (może nawet w kolejce?), Pozwala istniejącym zapytaniom zakończyć, uruchamia pakiet, a następnie ponownie otwiera tabelę w poszukiwaniu zapytań. Obiekt zarządzania może wywołać zdarzenie (BatchProcessingEvent), które warstwa interfejsu użytkownika może zinterpretować, aby poinformować użytkowników, że tabela jest obecnie niedostępna.

My $ 0,02,

Nate

+0

Rozważaliśmy to i bardzo nam się podoba ta opcja, ale wymaga to dużo więcej pracy, a czas to luksus, którego niestety nie mamy. Ale zgadzam się na to, bez względu na to, czy proces wsadowy serwera jest optymalny. –

0

Robimy to w naszych systemach wysokiej użytkowania i nie miałem żadnych problemów. Jednak, podobnie jak w przypadku wszystkich baz danych, jedynym sposobem, aby upewnić się, że byłoby pomocne byłoby wprowadzenie zmian w dev, a następnie załadowanie go przetestować. Nie wiedząc, że Twój pakiet SSIS nie działa, może nadal powodować blokadę.

1

Jednym z możliwych rozwiązań byłoby zminimalizowanie czasu potrzebnego na aktualizację tabeli.

Najpierw utworzę tabelę pomostową, aby pobrać dane z magazynu.

Jeśli trzeba zrobić „wkładki, aktualizacje i usuwa” w końcowej tabeli

Pozwala przypuszczać finał tabela wygląda następująco:

Table Products: 
    ProductId  int 
    QuantityOnHand Int 

i trzeba aktualizować QuantityOnHand z magazynu.

najpierw utworzyć tabeli tymczasowej takich jak:

Table Prodcuts_WareHouse 
    ProductId  int 
    QuantityOnHand Int 

a następnie utwórz "działania" tabela tak:

Table Prodcuts_Actions 
    ProductId  int 
    QuantityOnHand Int 
    Action   Char(1) 

Proces aktualizacji powinien wtedy być coś takiego:

1. Przytnij tabelę Prodcuts_WareHouse

2. Przytnij tabelę Prodcuts_Actions

3.Fill Prodcuts_WareHouse tabelę z danymi z magazynu

4.Wypełnić tabelę Prodcuts_Actions z tym:

wkładki:

INSERT INTO Prodcuts_Actions (ProductId, QuantityOnHand,Action) 
SELECT  SRC.ProductId, SRC.QuantityOnHand, 'I' AS ACTION 
FROM   Prodcuts_WareHouse AS SRC LEFT OUTER JOIN 
         Products AS DEST ON SRC.ProductId = DEST.ProductId 
WHERE  (DEST.ProductId IS NULL) 

Usuwa

INSERT INTO Prodcuts_Actions (ProductId, QuantityOnHand,Action) 
SELECT  DEST.ProductId, DEST.QuantityOnHand, 'D' AS Action 
FROM   Prodcuts_WareHouse AS SRC RIGHT OUTER JOIN 
         Products AS DEST ON SRC.ProductId = DEST.ProductId 
WHERE  (SRC.ProductId IS NULL) 

Aktualizacje

INSERT INTO Prodcuts_Actions (ProductId, QuantityOnHand,Action) 
SELECT  SRC.ProductId, SRC.QuantityOnHand, 'U' AS Action 
FROM   Prodcuts_WareHouse AS SRC INNER JOIN 
         Products AS DEST ON SRC.ProductId = DEST.ProductId AND SRC.QuantityOnHand <> DEST.QuantityOnHand 

Do tej pory nie zostały zablokowane końcowej tabeli.

5.In transakcja zaktualizować stołu finałowego:

BEGIN TRANS 

DELETE Products FROM Products INNER JOIN 
Prodcuts_Actions ON Products.ProductId = Prodcuts_Actions.ProductId 
WHERE  (Prodcuts_Actions.Action = 'D') 

INSERT INTO Prodcuts (ProductId, QuantityOnHand) 
SELECT ProductId, QuantityOnHand FROM Prodcuts_Actions WHERE Action ='I'; 

UPDATE Products SET QuantityOnHand = SRC.QuantityOnHand 
FROM   Products INNER JOIN 
Prodcuts_Actions AS SRC ON Products.ProductId = SRC.ProductId 
WHERE  (SRC.Action = 'U') 

COMMIT TRAN 

Z całym procesie powyżej, można zminimalizować ilość zapisów zostać zaktualizowany do niezbędnego minimum, a więc czas ostateczna tabela zostanie zablokowany podczas aktualizacji.

Możesz nawet nie używać transakcji w ostatnim kroku, więc pomiędzy rozkazem tabela zostanie zwolniona.

1

Jeśli masz wersję Enterprise Edition programu SQL Server do swojej dyspozycji, to proponuję użyć technologii partycjonowania SQL Server.

Możliwe, że aktualnie wymagane dane będą znajdować się na partycji "Na żywo" i na zaktualizowanej wersji danych na partycji "Wtórne" (która nie jest dostępna do wysyłania zapytań, ale do administrowania danymi).

Po zaimportowaniu danych do "Drugorzędnej" można natychmiast PRZEŁĄCZYĆ partycję "NA ŻYWO" OUT i "Drugorzędną" partycję IN, co spowoduje zero przestojów i brak blokowania.

Po wprowadzeniu przełącznika można obcinać niepotrzebne dane bez adwersa, które mają wpływ na użytkowników danych nowo uruchomionych (poprzednio na partycji dodatkowej).

Za każdym razem, gdy trzeba wykonać zadanie importu, wystarczy powtórzyć/odwrócić proces.

Aby dowiedzieć się więcej na temat SQL Server Partycjonowanie patrz:

http://msdn.microsoft.com/en-us/library/ms345146(SQL.90).aspx

Albo można po prostu zapytać mnie :-)

EDIT:

Na marginesie, w celu aby rozwiązać wszelkie problemy z blokowaniem, można użyć technologii wierszy SQL Server Row Versioning.

http://msdn.microsoft.com/en-us/library/ms345124(SQL.90).aspx

+0

To jest idea tego, co chcieliśmy osiągnąć z widokiem, ale mamy tylko standardową edycję SQL Server. –

2

Wystarczy przeczytać używasz SSIS

można użyć komponentu TableDiference od: http://www.sqlbi.eu/Home/tabid/36/ctl/Details/mid/374/ItemID/0/Default.aspx

alt text http://www.sqlbi.eu/Portals/0/Articles/Table%20Difference%20Images/DataFlowSimple.png

W ten sposób można zastosować zmiany do stołu, jeden po JEDEN, ale oczywiście będzie to znacznie wolniejsze iw zależności od rozmiaru stołu będzie wymagało więcej pamięci RAM na serwerze, ale problem z blokowaniem zostanie całkowicie poprawiony.

0

Jeśli tabela nie jest zbyt duża, można buforować dane w aplikacji przez krótki czas. Nie może całkowicie wyeliminować blokowania, ale zmniejszyłoby prawdopodobieństwo, że tabela zostanie zapytana, gdy nastąpi aktualizacja.

0

Być może sensownym byłoby przeprowadzenie analizy procesów, które są blokowane, ponieważ wydają się być częścią twojego krajobrazu, który się zmienił. Wystarczy jedno źle napisane zapytanie, aby utworzyć bloki, które widzisz. Jeśli nie masz źle napisanego zapytania, być może tabela wymaga jednego lub więcej indeksów pokrycia, aby przyspieszyć te zapytania i przywrócić Cię do działania bez konieczności przeprojektowywania już działającego kodu.

Nadzieja to pomaga,

Bill

Powiązane problemy