sql >> Databáze >  >> RDS >> Sqlserver

Obnovení tabulek SQL Server s menším přerušením pomocí přepínání oddílů

Běžným požadavkem v ETL a různých scénářích sestavování je tiché načtení pracovní tabulky SQL Serveru na pozadí, takže uživatelé dotazující se na data nejsou ovlivněni zápisy a naopak. Trik je v tom, jak a kdy uživatele nasměrujete na novou, obnovenou verzi dat.

Zjednodušený příklad pracovního stolu:Analogie farmářského trhu

Co je tedy pracovní tabulka v SQL? Předváděcí stůl lze snáze pochopit na příkladu ze skutečného světa:Řekněme, že máte stůl plný zeleniny, kterou prodáváte na místním farmářském trhu. Jak se vaše zelenina prodává a vy přinášíte nové zásoby:

  • Když přinesete spoustu nové zeleniny, zabere vám 20 minut, než sklidíte ze stolu a vyměníte zbývající zásoby za novější produkt.
  • Nechcete, aby tam zákazníci seděli a čekali 20 minut, než dojde k přechodu, protože většina dostane zeleninu jinde.

Co kdybyste měli druhý prázdný stůl, kam nakládáte novou zeleninu, a zatímco to děláte, zákazníci si stále mohou koupit starší zeleninu z prvního stolu? (Představme si, že to není proto, že by se starší zelenina pokazila nebo je jinak méně žádoucí.)

Obnovení tabulek na serveru SQL Server

Existuje několik metod, jak znovu načíst celé tabulky, zatímco jsou aktivně dotazovány; před dvěma desetiletími jsem bezuzdně využil výhody sp_rename — Hrál bych shellovou hru s prázdnou stínovou kopií stolu, šťastně bych stínovou kopii znovu načetl a pak teprve provedl přejmenování v rámci transakce.

V SQL Server 2005 jsem začal používat schémata k uchovávání stínových kopií tabulek, které jsem jednoduše přenesl pomocí stejné techniky shellové hry, o které jsem psal v těchto dvou příspěvcích:

  • Trick Shots:Schema Switch-a-Roo
  • Schéma Switch-a-Roo, část 2

Jedinou výhodou přenosu objektů mezi schématy oproti jejich přejmenování je, že neexistují žádné varovné zprávy o přejmenování objektu – což samo o sobě není ani problém, kromě toho, že varovné zprávy zaplňují protokoly historie agentů mnohem rychleji.

Oba přístupy stále vyžadují zámek modifikace schématu (Sch-M), takže musí čekat, až všechny existující transakce uvolní své vlastní zámky. Jakmile získají svůj zámek Sch-M, zablokují všechny následné dotazy vyžadující zámky stability schématu (Sch-S)… což je téměř každý dotaz. Může se rychle stát noční můrou blokovacího řetězce, protože jakékoli nové dotazy vyžadující Sch-S se musí dostat do fronty za Sch-M. (A ne, nemůžete to obejít pomocí RCSI nebo NOLOCK všude, protože i tyto dotazy stále vyžadují Sch-S. Sch-S nemůžete získat se Sch-M na místě, protože jsou nekompatibilní – o tom zde mluví Michael J. Swart.)

Kendra Little mi ve svém příspěvku „Staging Data:Locking Danger with ALTER SCHEMA TRANSFER“ skutečně otevřela oči ohledně nebezpečí přenosu schématu. Tam ukazuje, proč může být přenos schématu horší než přejmenování. Později popsala třetí a mnohem méně účinný způsob výměny tabulek, který nyní používám výhradně já:přepínání oddílů. Tato metoda umožňuje přepínači čekat s nižší prioritou, což není možné ani u technik přejmenování nebo přenosu schématu. Joe Sack podrobně popsal toto vylepšení přidané zpět do SQL Server 2014:„Zkoumání možností čekání na zámek s nízkou prioritou v SQL Server 2014 CTP1.“

Příklad přepínání oddílů serveru SQL

Podívejme se na základní příklad, který bude následovat po důkladné podstatě Kendry zde. Nejprve vytvoříme dvě nové databáze:

CREATE DATABASE NewWay;CREATE DATABASE OldWay;GO

V nové databázi vytvoříme stůl pro náš inventář zeleniny a dvě kopie stolu pro naši hru shell:

POUŽÍVEJTE NewWay;GO CREATE TABLE dbo.Vegetables_NewWay( VegetableID int, Name sysname, WhenPicked datetime, BackStory nvarchar(max));GO -- potřebujeme vytvořit dvě další kopie tabulky. CREATE TABLE dbo.Vegetables_NewWay_prev( VegetableID int, Name sysname, WhenPicked datetime, BackStory nvarchar(max));GO CREATE TABLE dbo.Vegetables_NewWay_hold( VegetableID int, Name sysname, WhenPicked datetime, BackStory) datetime, BackStory) 

Vytvoříme proceduru, která načte pracovní kopii tabulky a poté pomocí transakce přepne aktuální kopii ven.

CREATE PROCEDURE dbo.DoTheVeggieSwap_NewWayASBEGIN SET NOCOUNT ON; TRUNCATE TABLE dbo.Vegetables_NewWay_prev; INSERT dbo.Vegetables_NewWay_prev SELECT TOP (1000000) s.session_id, o.name, s.last_successful_logon, LEFT(m.definition, 500) FROM sys.dm_exec_sessions JAKO s CROSS model_JOIN modelsIN o.sys.all all_sql_modules AS m ON o.[id_objektu] =m.[id_objektu]; -- zde je třeba vzít zámky Sch-M:ZAČÁTEK TRANSAKCI; ALTER TABLE dbo.Vegetables_NewWay PŘEPNOUT NA dbo.Vegetables_NewWay_hold WITH (WAIT_AT_LOW_PRIORITY (MAX_DURATION =1 MINUTA, ABORT_AFTER_WAIT =BLOCKERS)); ALTER TABLE dbo.Vegetables_NewWay_prev PŘEPNOUT NA dbo.Vegetables_NewWay; ZAKÁZAT TRANSAKCI; -- a nyní se uživatelé budou dotazovat na nová data v dbo -- mohou přepnout starou kopii zpět a zkrátit ji -- bez zasahování do jiných dotazů ALTER TABLE dbo.Vegetables_NewWay_hold PŘEPNOUT NA dbo.Vegetables_NewWay_prev; TRUNCATE TABLE dbo.Vegetables_NewWay_prev;ENDGO

Krása WAIT_AT_LOW_PRIORITY je, že můžete zcela ovládat chování pomocí ABORT_AFTER_WAIT možnost:

ABORT_AFTER_WAIT
nastavení
Popis / příznaky
JÁ SE To znamená, že se přepínač vzdá za n minut.

U relace, která se pokouší provést přepnutí, se toto objeví jako chybová zpráva:

Časový limit požadavku na zámek byl překročen.
BLOKÁTORY To znamená, že přepínač bude čekat až n minut, pak se probojuje do čela řady zabije všechny blokátory před ní .

Relace, které se pokoušejí interagovat s tabulkou, které jsou zasaženy operací přepínače, uvidí některé kombinace těchto chybových zpráv:

Vaše relace byla odpojena z důvodu operace DDL s vysokou prioritou.

V provádění nelze pokračovat, protože relace je ve stavu ukončení.

U aktuálního příkazu došlo k závažné chybě. Výsledky, pokud existují, by měly být vyřazeny.

ŽÁDNÉ To říká, že přepínač bude šťastně čekat, až na něj přijde řada, bez ohledu na MAX_DURATION .

Toto je stejné chování, jaké byste získali při přejmenování, přenosu schématu nebo přepnutí oddílu bez WAIT_AT_LOW_PRIORITY .

BLOCKERS Tato možnost není nejpřívětivější způsob, jak věci řešit, protože už říkáte, že je v pořádku, aby uživatelé viděli data, která jsou trochu zastaralá, prostřednictvím této operace přípravy/přepnutí. Pravděpodobně bych raději použil SELF a nechte operaci zopakovat v případech, kdy nemohla získat požadované zámky ve stanoveném čase. Sledoval bych, jak často to selže, zejména selhání po sobě, protože chcete mít jistotu, že data nikdy nebudou příliš zastaralá.

Ve srovnání se starým způsobem přepínání mezi schématy

Zde je návod, jak bych dříve řešil přepínání:

POUŽÍVEJTE OldWay;GO -- vytvořte dvě schémata a dvě kopie tabulky VYTVOŘIT SCHÉMA předchozí AUTORIZACE dbo;GO VYTVOŘIT SCHÉMA hold AUTORIZACE dbo;GO VYTVOŘIT TABULKU dbo.Vegetables_OldWay( VegetableID int, Name sysname, Back(Picked date) max));GO CREATE TABLE prev.Vegetables_OldWay( VegetableID int, Name sysname, WhenPicked datetime, BackStory nvarchar(max));GO CREATE PROCEDURE dbo.DoTheVeggieSwap_OldWayASBEGIN SET NOCOUNT ON; TRUNCATE TABLE předchozí.Vegetables_OldWay; INSERT prev.Vegetables_OldWay SELECT TOP (1000000) s.session_id, o.name, s.last_successful_logon, LEFT(m.definition, 500) FROM sys.dm_exec_sessions AS s CROSS JOIN model.sys.IN JOsysject. all_sql_modules AS m ON o.[id_objektu] =m.[id_objektu]; -- zde je třeba vzít zámky Sch-M:ZAČÁTEK TRANSAKCI; ALTER SCHEMA hold TRANSFER dbo.Vegetables_OldWay; ALTER SCHEMA dbo TRANSFER prev.Vegetables_OldWay; ZAKÁZAT TRANSAKCI; -- a nyní se uživatelé budou dotazovat na nová data v dbo -- mohou přenést starou kopii zpět a zkrátit ji, aniž by -- zasahovali do jiných dotazů:ALTER SCHEMA předchozí TRANSFER hold.Vegetables_OldWay; TRUNCATE TABLE prev.Vegetables_OldWay;ENDGO

Provedl jsem testy souběžnosti pomocí dvou oken SQLQueryStress Erika Ejlskova Jensena:jedno pro opakování volání procedury každou minutu a druhé pro spuštění 16 vláken, jako je tato, tisíckrát:

ZAČÁTEK TRANSAKCE; UPDATE TOP (1) dbo. SET name +='x';SELECT TOP (10) name FROM dbo.
ORDER BY NEWID();WAITFOR DELAY '00:00:02'; ZAKÁZAT TRANSAKCI;

Můžete se podívat na výstup z SQLQueryStress nebo sys.dm_exec_query_stats nebo Query Store a uvidíte něco ve smyslu následujících výsledků (ale vřele doporučuji použít kvalitní nástroj pro sledování výkonu SQL Serveru, pokud to myslíte vážně proaktivní optimalizace databázových prostředí):

Trvání a chybovost Přenos schématu ABORT_AFTER_WAIT:
SELF
ABORT_AFTER_WAIT:
BLOCKERS
Průměrná doba trvání – přenos/přepnutí 96,4 sekund 68,4 sekund 20,8 sekund
Průměrná doba trvání – DML 18,7 sekund 2,7 sekundy 2,9 sekundy
Výjimky – Přenést/přepnout 0 0,5/minutu 0
Výjimky – DML 0 0 25,5/minutu

Všimněte si, že trvání a počty výjimek budou velmi záviset na specifikacích vašeho serveru a na tom, co se děje ve vašem prostředí. Všimněte si také, že i když neexistovaly žádné výjimky pro testy přenosu schématu při použití SQLQueryStress, můžete v závislosti na náročné aplikaci dosáhnout některých přísnějších časových limitů. A bylo to v průměru o tolik pomalejší, protože se blokování hromadilo mnohem agresivněji. Nikdo nikdy nechce výjimky, ale když dojde k takovému kompromisu, možná upřednostníte pár výjimek (v závislosti na frekvenci obnovovacích operací), než aby všichni neustále čekali déle.

Přepínání oddílů vs. Přejmenování/přenos schématu na obnovení tabulek SQL Server

Přepínání oddílů vám umožňuje vybrat si, která část vašeho procesu nese náklady na souběžnost. Můžete dát přednost procesu přepínání, takže data jsou spolehlivější čerstvá, ale to znamená, že některé z vašich dotazů selžou. Naopak můžete upřednostnit dotazy za cenu pomalejšího procesu obnovování (a občasného selhání). Hlavním cílem je přepínání oddílů serveru SQL Server je vynikající metodou pro obnovení tabulek serveru SQL Server ve srovnání s předchozími technikami přejmenování/přenosu schémat téměř ve všech bodech a můžete použít robustnější logiku opakování nebo experimentovat s tolerancemi trvání, abyste dosáhli konečného cíle. pro vaši pracovní zátěž.


  1. Jak získat nové uživatele za den v MySQL

  2. java.sql.SQLException:I/O Error:Resetování připojení na linuxovém serveru

  3. MySQL „NOT IN“ dotazuje 3 tabulky

  4. Jaký je rozdíl mezi AS a IS v uložené proceduře Oracle?