Database
 sql >> Baza danych >  >> RDS >> Database

T-SQL Wtorek #33 :Trick Shots :Schemat Switch-A-Roo

W tym miesiącu we wtorek T-SQL poprowadzi Mike Fal (blog | twitter), a tematem są Trick Shots, gdzie zapraszamy do opowiedzenia społeczności o niektórych rozwiązaniach zastosowanych w SQL Server, które przynajmniej dla nas jako rodzaj „podstępnego strzału” – coś podobnego do masowego, „angielskiego” lub skomplikowanych uderzeń bankowych w bilard lub snooker. Po 15 latach pracy z SQL Serverem miałem okazję wymyślić sztuczki, które rozwiążą kilka całkiem interesujących problemów, ale taki, który wydaje się być dość wielokrotnego użytku, łatwo przystosowuje się do wielu sytuacji i jest prosty we wdrożeniu, jest coś, co nazywam „przełączaniem schematu”.

Załóżmy, że masz scenariusz, w którym masz dużą tabelę przeglądową, która musi być okresowo odświeżana. Ta tabela przeglądowa jest potrzebna na wielu serwerach i może zawierać dane, które są wypełniane z zewnętrznego lub zewnętrznego źródła, np. Dane IP lub domeny lub mogą reprezentować dane z własnego środowiska.

Pierwsze kilka scenariuszy, w których potrzebowałem rozwiązania, polegało na udostępnieniu metadanych i zdenormalizowanych danych tylko do odczytu „pamięci podręcznej danych” – tak naprawdę tylko instancjom SQL Server MSDE (i później Express) zainstalowanym na różnych serwerach internetowych, więc serwery internetowe ściągnęły to buforowane dane lokalnie, zamiast zawracać sobie głowę podstawowym systemem OLTP. Może się to wydawać zbędne, ale odciążenie operacji odczytu z głównego systemu OLTP i możliwość całkowitego usunięcia połączenia sieciowego z równania doprowadziło do prawdziwego wzrostu ogólnej wydajności, a przede wszystkim dla użytkowników końcowych .

Serwery te nie potrzebowały najświeższych kopii danych; w rzeczywistości wiele tabel pamięci podręcznej było aktualizowanych codziennie. Ale ponieważ systemy działały 24×7, a niektóre z tych aktualizacji mogły zająć kilka minut, często przeszkadzały prawdziwym klientom, którzy robili prawdziwe rzeczy w systemie.

Pierwotne podejście(-a)

Na samym początku kod był dość uproszczony:usunęliśmy wiersze, które zostały usunięte ze źródła, zaktualizowaliśmy wszystkie wiersze, o których mogliśmy stwierdzić, że uległy zmianie, i wstawiliśmy wszystkie nowe wiersze. Wyglądało to mniej więcej tak (obsługa błędów itp. została usunięta dla zwięzłości):

BEGIN TRANSACTION;
 
DELETE dbo.Lookup 
  WHERE [key] NOT IN 
  (SELECT [key] FROM [source]);
 
UPDATE d SET [col] = s.[col]
  FROM dbo.Lookup AS d
  INNER JOIN [source] AS s
  ON d.[key] = s.[key]
  -- AND [condition to detect change];
 
INSERT dbo.Lookup([cols]) 
  SELECT [cols] FROM [source]
  WHERE [key] NOT IN 
  (SELECT [key] FROM dbo.Lookup);
 
COMMIT TRANSACTION;

Nie trzeba dodawać, że ta transakcja może powodować pewne rzeczywiste problemy z wydajnością, gdy system był w użyciu. Z pewnością były na to inne sposoby, ale każda metoda, którą wypróbowaliśmy, była równie powolna i kosztowna. Jak wolno i drogo? „Pozwól, że policzę skany…”

Ponieważ to wcześniejsze MERGE, a my już odrzuciliśmy „zewnętrzne” podejścia, takie jak DTS, po kilku testach ustaliliśmy, że bardziej wydajne byłoby po prostu wyczyszczenie tabeli i ponowne jej wypełnienie, zamiast próbować zsynchronizować ją ze źródłem :

BEGIN TRANSACTION;
 
TRUNCATE TABLE dbo.Lookup;
 
INSERT dbo.Lookup([cols]) 
  SELECT [cols] FROM [source];
 
COMMIT TRANSACTION;

Teraz, jak wyjaśniłem, to zapytanie z [źródło] może zająć kilka minut, zwłaszcza jeśli wszystkie serwery WWW były aktualizowane równolegle (próbowaliśmy przesunąć się tam, gdzie mogliśmy). A jeśli klient był w witrynie i próbował uruchomić zapytanie dotyczące tabeli wyszukiwania, musiał czekać na zakończenie tej transakcji. W większości przypadków, jeśli uruchamiają to zapytanie o północy, nie ma znaczenia, czy otrzymali wczorajszą kopię danych wyszukiwania, czy dzisiejszą; więc zmuszanie ich do czekania na odświeżenie wydawało się głupie i faktycznie doprowadziło do wielu wezwań pomocy technicznej.

Więc chociaż było to lepsze, z pewnością dalekie od ideału.

Moje rozwiązanie początkowe:sp_rename

Moim pierwszym rozwiązaniem, w czasach, gdy SQL Server 2000 był fajny, było utworzenie tabeli „cieni”:

CREATE TABLE dbo.Lookup_Shadow([cols]);

W ten sposób mogłem wypełnić tabelę cieni bez przerywania użytkownikom, a następnie wykonać trójstronną zmianę nazwy – szybką operację tylko na metadanych – dopiero po zakończeniu wypełniania. Coś takiego (ponownie, rażąco uproszczone):

TRUNCATE TABLE dbo.Lookup_Shadow;
 
INSERT dbo.Lookup_Shadow([cols]) 
  SELECT [cols] FROM [source];
 
BEGIN TRANSACTION;
 
  EXEC sp_rename N'dbo.Lookup',        N'dbo.Lookup_Fake';
  EXEC sp_rename N'dbo.Lookup_Shadow', N'dbo.Lookup';
 
COMMIT TRANSACTION;
 
-- if successful:
EXEC sp_rename N'dbo.Lookup_Fake', N'dbo.Lookup_Shadow';

Wadą tego początkowego podejścia było to, że sp_rename zawiera komunikat wyjściowy, którego nie można wygasić, ostrzegający o zagrożeniach związanych ze zmianą nazw obiektów. W naszym przypadku wykonaliśmy to zadanie za pomocą zadań SQL Server Agent i obsłużyliśmy wiele metadanych i innych tabel pamięci podręcznej, więc historia zadań została zalana wszystkimi tymi bezużytecznymi wiadomościami i faktycznie spowodowała obcięcie rzeczywistych błędów ze szczegółów historii. (Złożyłem skargę na to w 2007 roku, ale moja sugestia została ostatecznie odrzucona i zamknięta jako „nie da się naprawić”).

Lepsze rozwiązanie:schematy

Po uaktualnieniu do SQL Server 2005 odkryłem fantastyczne polecenie o nazwie CREATE SCHEMA. Zaimplementowanie tego samego typu rozwiązania przy użyciu schematów zamiast zmiany nazw tabel było trywialne, a teraz historia agenta nie byłaby zanieczyszczona tymi wszystkimi nieprzydatnymi komunikatami. Zasadniczo stworzyłem dwa nowe schematy:

CREATE SCHEMA fake   AUTHORIZATION dbo;
CREATE SCHEMA shadow AUTHORIZATION dbo;

Następnie przeniosłem tabelę Lookup_Shadow do schematu pamięci podręcznej i zmieniłem jej nazwę:

ALTER SCHEMA shadow TRANSFER dbo.Lookup_Shadow;
 
EXEC sp_rename N'shadow.Lookup_Shadow', N'Lookup';

(Jeśli dopiero wdrażasz to rozwiązanie, utworzysz nową kopię tabeli w schemacie, nie przenosząc tam istniejącej tabeli i zmieniając jej nazwę.)

Mając te dwa schematy na miejscu i kopię tabeli Lookup w schemacie cienia, moja trójstronna zmiana nazwy stała się trójstronnym transferem schematu:

TRUNCATE TABLE shadow.Lookup;
 
INSERT shadow.Lookup([cols]) 
  SELECT [cols] FROM [source];
 
-- perhaps an explicit statistics update here
 
BEGIN TRANSACTION;
 
  ALTER SCHEMA fake TRANSFER     dbo.Lookup;
  ALTER SCHEMA dbo  TRANSFER  shadow.Lookup;
 
COMMIT TRANSACTION;
 
ALTER SCHEMA shadow TRANSFER fake.Lookup;

W tym momencie możesz oczywiście opróżnić kopię w tle tabeli, jednak w niektórych przypadkach przydatne jest pozostawienie „starej” kopii danych w celu rozwiązywania problemów:

TRUNCATE TABLE shadow.Lookup;

Cokolwiek dalej robisz z kopią w tle, upewnij się, że robisz to poza transakcją – dwie operacje transferu powinny być tak zwięzłe i szybkie, jak to możliwe.

Niektóre zastrzeżenia

  • Klucze obce
    To nie zadziała od razu, jeśli do tabeli odnośników odwołują się klucze obce. W naszym przypadku nie wskazaliśmy żadnych ograniczeń na te tabele pamięci podręcznej, ale jeśli to zrobisz, być może będziesz musiał trzymać się natrętnych metod, takich jak MERGE. Lub użyj metod tylko z dołączaniem i wyłącz lub upuść klucze obce przed wykonaniem jakichkolwiek modyfikacji danych (a następnie ponownie je utwórz lub ponownie włącz). Jeśli trzymasz się technik MERGE / UPSERT i robisz to między serwerami lub, co gorsza, z systemu zdalnego, gorąco polecam pobieranie surowych danych lokalnie, zamiast próbować używać tych metod między serwerami.
  • Statystyki
    Przełączanie tabel (przy użyciu zmiany nazwy lub przeniesienia schematu) spowoduje przeskakiwanie statystyk między dwiema kopiami tabeli, co oczywiście może stanowić problem dla planów. Możesz więc rozważyć dodanie wyraźnych aktualizacji statystyk w ramach tego procesu.
  • Inne podejścia
    Istnieją oczywiście inne sposoby, których po prostu nie miałem okazji wypróbować. Przełączanie partycji i używanie widoku + synonim to dwa podejścia, które mogę zbadać w przyszłości w celu dokładniejszego potraktowania tematu. Chciałbym usłyszeć twoje doświadczenia i sposób, w jaki rozwiązałeś ten problem w swoim środowisku. I tak, zdaję sobie sprawę, że ten problem jest w dużej mierze rozwiązywany przez grupy dostępności i czytelne pliki pomocnicze w SQL Server 2012, ale uważam to za „podstęp”, jeśli można rozwiązać problem bez rzucania na niego licencji high-end lub replikowania całą bazę danych, aby kilka tabel było zbędnych. :-)

Wniosek

Jeśli potrafisz żyć z ograniczeniami w tym zakresie, to podejście może być skuteczniejsze niż scenariusz, w którym zasadniczo przenosisz tabelę do trybu offline za pomocą SSIS lub własnej procedury MERGE / UPSERT, ale pamiętaj, aby przetestować obie techniki. Najważniejszym punktem jest to, że użytkownik końcowy uzyskujący dostęp do stołu powinien mieć dokładnie takie same wrażenia o każdej porze dnia, nawet jeśli trafi na stół w trakcie okresowej aktualizacji.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wypełnianie Teradata realistycznymi danymi testowymi De Novo

  2. Jaka jest różnica między RANK a DENSE_RANK w SQL?

  3. Używanie Microsoft DiskSpd do testowania podsystemu pamięci masowej

  4. Włączanie uwierzytelniania dwuskładnikowego dla ScaleGrid DBaaS

  5. SQL SELECT dla początkujących