sql >> Databáze >  >> RDS >> Database

T-SQL Úterý #33:Trikové záběry:Schéma Switch-A-Roo

Úterý T-SQL tento měsíc pořádá Mike Fal (blog | twitter) a tématem jsou Trick Shots, kde jsme vyzváni, abychom komunitě řekli o nějakém řešení, které jsme použili v SQL Serveru, které, alespoň pro nás, jako jakýsi „trick shot“ – něco podobného jako použití massé, „anglických“ nebo komplikovaných bankovních úderů v kulečníku nebo snookeru. Po 15 letech práce se serverem SQL jsem měl příležitost přijít s triky, jak vyřešit některé docela zajímavé problémy, ale jeden, který se zdá být docela znovu použitelný, snadno se přizpůsobí mnoha situacím a snadno se implementuje, je něco, čemu říkám "schema switch-a-roo."

Řekněme, že máte scénář, ve kterém máte velkou vyhledávací tabulku, kterou je třeba pravidelně obnovovat. Tato vyhledávací tabulka je potřebná na mnoha serverech a může obsahovat data, která se naplní z externího zdroje nebo zdroje třetí strany, např. IP nebo data domény, nebo mohou představovat data z vašeho vlastního prostředí.

Prvních pár scénářů, kdy jsem pro to potřeboval řešení, bylo zpřístupnění metadat a denormalizovaných dat do „datových mezipamětí“ pouze pro čtení – ve skutečnosti pouze instance SQL Server MSDE (a později Express) nainstalované na různých webových serverech, takže webové servery stáhly tato data ukládala do mezipaměti lokálně, místo aby obtěžovala primární systém OLTP. To se může zdát nadbytečné, ale snížení zátěže čtení od primárního systému OLTP a možnost úplně vyřadit síťové připojení z rovnice vedlo ke skutečnému nárůstu celkového výkonu a především pro koncové uživatele. .

Tyto servery nepotřebovaly aktuální kopie dat; ve skutečnosti bylo mnoho tabulek mezipaměti aktualizováno pouze denně. Ale protože systémy byly 24×7 a některé z těchto aktualizací mohly trvat několik minut, často překážely skutečným zákazníkům, kteří na systému dělali skutečné věci.

Původní přístup(y)

Na samém začátku byl kód poněkud zjednodušený:smazali jsme řádky, které byly odstraněny ze zdroje, aktualizovali jsme všechny řádky, o kterých jsme věděli, že se změnily, a vložili jsme všechny nové řádky. Vypadalo to nějak takto (pro stručnost odstraněno zpracování chyb atd.):

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;

Netřeba dodávat, že tato transakce mohla způsobit skutečné problémy s výkonem, když byl systém používán. Určitě byly jiné způsoby, jak to udělat, ale každá metoda, kterou jsme vyzkoušeli, byla stejně pomalá a drahá. Jak pomalé a drahé? „Nechte mě spočítat skeny…“

Vzhledem k tomu, že toto staré MERGE a my jsme již zavrhli „externí“ přístupy, jako je DTS, určitým testováním jsme zjistili, že by bylo efektivnější tabulku jednoduše vymazat a znovu ji vyplnit, než se pokoušet o synchronizaci se zdrojem. :

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

Nyní, jak jsem vysvětlil, tento dotaz od [zdroj] může trvat několik minut, zvláště pokud byly všechny webové servery aktualizovány paralelně (snažili jsme se potácet, kde jsme mohli). A pokud byl zákazník na webu a pokoušel se spustit dotaz zahrnující vyhledávací tabulku, musel počkat na dokončení transakce. Ve většině případů, pokud spouštějí tento dotaz o půlnoci, ve skutečnosti by nezáleželo na tom, zda získali včerejší kopii vyhledávacích dat nebo dnešní; takže nechat je čekat na aktualizaci vypadalo hloupě a ve skutečnosti to vedlo k řadě telefonátů na podporu.

Takže i když to bylo lepší, rozhodně to nemělo k dokonalosti daleko.

Moje počáteční řešení:sp_rename

Moje počáteční řešení, když byl SQL Server 2000 v pohodě, bylo vytvořit „stínovou“ tabulku:

CREATE TABLE dbo.Lookup_Shadow([cols]);

Tímto způsobem jsem mohl naplnit stínovou tabulku, aniž bych vůbec přerušil uživatele, a poté provést třícestné přejmenování – rychlou operaci pouze s metadaty – až po dokončení populace. Něco takového (opět hrubě zjednodušené):

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';

Nevýhodou tohoto původního přístupu bylo, že sp_rename má nepotlačitelnou výstupní zprávu, která vás varuje před nebezpečím přejmenování objektů. V našem případě jsme tento úkol provedli prostřednictvím úloh SQL Server Agent a zpracovali jsme spoustu metadat a dalších tabulek mezipaměti, takže historie úloh byla zaplavena všemi těmito zbytečnými zprávami a ve skutečnosti způsobila, že skutečné chyby byly zkráceny z podrobností historie. (Stěžoval jsem si na to v roce 2007, ale můj návrh byl nakonec zamítnut a uzavřen jako „Nebude opraveno.“)

Lepší řešení:Schémata

Jakmile jsme upgradovali na SQL Server 2005, objevil jsem tento fantastický příkaz s názvem CREATE SCHEMA. Bylo triviální implementovat stejný typ řešení pomocí schémat místo přejmenování tabulek a historie agenta by nyní nebyla zamořena všemi těmito neužitečnými zprávami. V podstatě jsem vytvořil dvě nová schémata:

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

Potom jsem přesunul tabulku Lookup_Shadow do schématu mezipaměti a přejmenoval ji:

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

(Pokud právě implementujete toto řešení, vytvořili byste novou kopii tabulky ve schématu, nepřesunuli byste tam existující tabulku a nepřejmenovali ji.)

S těmito dvěma schématy a kopií vyhledávací tabulky ve stínovém schématu se mé třícestné přejmenování stalo třícestným převodem schématu:

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;

V tomto okamžiku můžete samozřejmě vyprázdnit stínovou kopii tabulky, ale v některých případech mi přišlo užitečné ponechat "starou" kopii dat pro účely řešení problémů:

TRUNCATE TABLE shadow.Lookup;

Cokoli dalšího, co uděláte se stínovou kopií, budete chtít udělat mimo transakci – obě převodové operace by měly být co nejstručnější a nejrychlejší.

Některá upozornění

  • Zahraniční klíče
    To nebude fungovat hned po vybalení, pokud na vyhledávací tabulku odkazují cizí klíče. V našem případě jsme na tyto tabulky mezipaměti neukázali žádná omezení, ale pokud ano, možná budete muset zůstat u rušivých metod, jako je MERGE. Nebo použijte metody pouze připojování a deaktivujte nebo zrušte cizí klíče před provedením jakýchkoli úprav dat (poté je znovu vytvořte nebo znovu povolte). Pokud se budete držet technik MERGE / UPSERT a děláte to mezi servery nebo, což je ještě horší, ze vzdáleného systému, důrazně doporučuji získat nezpracovaná data lokálně, než se pokoušet používat tyto metody mezi servery.
  • Statistiky
    Přepínání tabulek (pomocí přejmenování nebo přenosu schématu) povede k přepínání statistik tam a zpět mezi dvěma kopiemi tabulky, a to může být pro plány samozřejmě problém. Můžete tedy zvážit přidání explicitních aktualizací statistik jako součást tohoto procesu.
  • Další přístupy
    Existují samozřejmě i jiné způsoby, jak toho dosáhnout, které jsem prostě neměl příležitost vyzkoušet. Přepínání oddílů a použití pohledu + synonyma jsou dva přístupy, které mohu v budoucnu prozkoumat pro důkladnější zpracování tématu. Zajímalo by mě vaše zkušenosti a jak jste tento problém vyřešili ve svém okolí. A ano, uvědomuji si, že tento problém je z velké části vyřešen skupinami dostupnosti a čitelnými sekundárními servery v SQL Server 2012, ale považuji to za „nástřel“, pokud problém dokážete vyřešit, aniž byste na problém vrhli špičkové licence nebo replikovali celou databázi, aby bylo několik tabulek nadbytečných. :-)

Závěr

Pokud zde dokážete žít s omezeními, může být tento přístup lepší než scénář, kdy v podstatě převádíte tabulku do režimu offline pomocí SSIS nebo vlastní rutiny MERGE / UPSERT, ale nezapomeňte otestovat obě techniky. Nejdůležitějším bodem je, že koncový uživatel, který přistupuje k tabulce, by měl mít přesně stejnou zkušenost, kdykoli během dne, i když narazí na tabulku uprostřed vaší pravidelné aktualizace.


  1. Oracle After Delete Trigger... Jak se vyhnout Mutující tabulce (ORA-04091)?

  2. Jak získat kalendářní čtvrtletí z data v TSQL

  3. Získejte nejnovější datum ze seskupených dat MySQL

  4. Jak zjistit třetí nebo nᵗʰ maximální plat z platové tabulky?