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

Čitelné sekundární díly za rozpočet

Skupiny dostupnosti, představené v SQL Server 2012, představují zásadní posun ve způsobu, jakým přemýšlíme o vysoké dostupnosti a obnově po havárii pro naše databáze. Jednou ze skvělých věcí, které je zde umožněno, je přesunutí operací pouze pro čtení na sekundární repliku, takže primární instance čtení/zápisu není obtěžována otravnými věcmi, jako je hlášení koncových uživatelů. Nastavení není jednoduché, ale je mnohem snazší a lépe udržovatelné než předchozí řešení (zvedněte ruku, pokud se vám líbí nastavení zrcadlení a snímků a veškerá věčná údržba s tím spojená).

Lidé jsou velmi nadšení, když slyší o skupinách dostupnosti. Pak přijde realita:tato funkce vyžaduje Enterprise Edition serveru SQL Server (stejně jako SQL Server 2014). Enterprise Edition je drahá, zvláště pokud máte hodně jader, a zejména po odstranění licencování na základě CAL (pokud jste nebyli dědečkem z 2008 R2, v takovém případě jste omezeni na prvních 20 jader). Vyžaduje také Windows Server Failover Clustering (WSFC), což je komplikace nejen pro demonstraci technologie na notebooku, ale vyžaduje také Enterprise Edition systému Windows, řadič domény a celou řadu konfigurací pro podporu clusteringu. A také existují nové požadavky týkající se Software Assurance; dodatečné náklady, pokud chcete, aby vaše pohotovostní instance byly v souladu.

Někteří zákazníci nedokážou ospravedlnit cenu. Jiní vidí hodnotu, ale prostě si to nemohou dovolit. Co tedy mají tito uživatelé dělat?

Váš nový hrdina:Log Shipping

Přeprava kulatiny existuje již věky. Je to jednoduché a prostě to funguje. Skoro pořád. A kromě obcházení licenčních nákladů a konfiguračních překážek prezentovaných Availability Groups se může také vyhnout 14bajtové penalizaci, o které Paul Randal (@PaulRandal) hovořil tento týden v newsletteru SQLskills Insider (13. října 2014).

Jedním z problémů, se kterými se lidé potýkají s používáním dodané kopie protokolu jako čitelné sekundární stránky, je však to, že musíte vyhodit všechny stávající uživatele, abyste mohli použít nové protokoly – takže buďto uživatelé budou naštvaní, protože jsou opakovaně vyrušováni. od spouštění dotazů nebo máte uživatele, kteří jsou naštvaní, protože jejich data jsou zastaralá. Je to proto, že se lidé omezují na jeden čitelný sekundární díl.

Nemusí to tak být; Myslím, že zde existuje elegantní řešení, a i když to může vyžadovat mnohem více práce nohou než například zapnutí skupin dostupnosti, pro některé to bude jistě atraktivní možnost.

V zásadě můžeme nastavit několik sekundářů, do kterých budeme logovat loď a pouze jednoho z nich učiníme „aktivním“ sekundárním, za použití přístupu typu round-robin. Úloha, která odesílá protokoly, ví, který z nich je aktuálně aktivní, takže pouze obnovuje nové protokoly na "další" server pomocí WITH STANDBY volba. Aplikace pro vytváření sestav používá stejné informace k tomu, aby za běhu určila, jaký by měl být připojovací řetězec pro další sestavu, kterou uživatel spustí. Když je připravena další záloha protokolu, vše se posune o jednu a instance, která se nyní stane novou čitelnou sekundární, se obnoví pomocí WITH STANDBY .

Aby byl model nekomplikovaný, řekněme, že máme čtyři instance, které slouží jako čitelné sekundární položky, a každých 15 minut provádíme zálohy protokolů. V každém okamžiku budeme mít jeden aktivní sekundární v pohotovostním režimu s daty ne staršími než 15 minut a tři sekundární v pohotovostním režimu, které neobsluhují nové dotazy (ale stále mohou vracet výsledky pro starší dotazy).

To bude fungovat nejlépe, pokud se neočekává, že žádné dotazy budou trvat déle než 45 minut. (Tyto cykly možná budete muset upravit v závislosti na povaze vašich operací pouze pro čtení, na tom, kolik souběžných uživatelů spouští delší dotazy a zda je někdy možné vyrušit uživatele tím, že všechny vykopnete.)

Bude také nejlépe fungovat, pokud po sobě jdoucí dotazy spouštěné stejným uživatelem mohou změnit svůj připojovací řetězec (toto je logika, která bude muset být v aplikaci, i když můžete použít synonyma nebo pohledy v závislosti na architektuře) a obsahovat různá data, která mají se mezitím změnily (stejně jako kdyby se dotazovali na živou, neustále se měnící databázi).

S ohledem na všechny tyto předpoklady zde uvádíme ilustrativní sled událostí pro prvních 75 minut naší implementace:

čas události vizuální
12:00 (t0)
  • Záložní protokol t0
  • Vyřadit uživatele z instance A
  • Obnovte protokol t0 do instance A (STANDBY)
  • Nové dotazy pouze pro čtení přejdou do instance A
12:15 (t1)
  • Záložní protokol t1
  • Vyřadit uživatele z instance B
  • Obnovit protokol t0 do instance B (NORECOVERY)
  • Obnovte protokol t1 do instance B (STANDBY)
  • Nové dotazy pouze pro čtení přejdou do instance B
  • Stávající dotazy pouze pro čtení do instance A mohou pokračovat v běhu, ale s zpožděním ~15 minut
12:30 (t2)
  • Záložní protokol t2
  • Vyřadit uživatele z instance C
  • Obnovte protokoly t0 -> t1 do instance C (NORECOVERY)
  • Obnovte protokol t2 do instance C (STANDBY)
  • Nové dotazy pouze pro čtení přejdou do instance C
  • Stávající dotazy pouze pro čtení do instancí A a B mohou nadále běžet (za 15–30 minut)
12:45 (t3)
  • Záložní protokol t3
  • Vyřadit uživatele z instance D
  • Obnovte protokoly t0 -> t2 do instance D (NORECOVERY)
  • Obnovte protokol t3 do instance D (STANDBY)
  • Nové dotazy pouze pro čtení přejdou do instance D
  • Stávající dotazy pouze pro čtení do instancí A, B a C mohou pokračovat v běhu (za 15–45 minut)
13:00 (t4)
  • Záložní protokol t4
  • Vyřadit uživatele z instance A
  • Obnovte protokoly t1 -> t3 do instance A (NORECOVERY)
  • Obnovte protokol t4 do instance A (STANDBY)
  • Nové dotazy pouze pro čtení přejdou do instance A
  • Stávající dotazy pouze pro čtení do instancí B, C a D mohou pokračovat v běhu (15–45 minut pozadu)
  • Dotazy stále běžící na instanci A od t0 -> ~t1 (45–60 minut) budou zrušeny


To se může zdát dost jednoduché; psaní kódu, který zvládne vše, je trochu skličující. Hrubý přehled:

  1. Na primárním serveru (budu mu říkat BOSS ), vytvořte databázi. Než budete přemýšlet o dalším postupu, zapněte Trace Flag 3226, abyste zabránili tomu, aby zprávy o úspěšném zálohování zasypaly chybový protokol SQL Serveru.
  2. Na BOSS , přidejte propojený server pro každý sekundární server (budu jim říkat PEON1 -> PEON4 ).
  3. Někde přístupné všem serverům vytvořte sdílenou složku pro ukládání záloh databáze/protokolů a zajistěte, aby servisní účty pro každou instanci měly přístup pro čtení i zápis. Každá sekundární instance také musí mít určené umístění pro pohotovostní soubor.
  4. V samostatné nástrojové databázi (nebo MSDB, chcete-li) vytvořte tabulky, které budou obsahovat konfigurační informace o databázi (databázích), všech sekundárních souborech a protokolování historie zálohování a obnovy.
  5. Vytvořte uložené procedury, které budou zálohovat databázi a obnovovat do sekundárních souborů S NORECOVERY a poté použijte jeden protokol S STANDBY a označte jednu instanci jako aktuální sekundární pohotovostní režim. Tyto postupy lze také použít k opětovné inicializaci celého nastavení odesílání protokolu v případě, že se něco pokazí.
  6. Vytvořte úlohu, která se bude spouštět každých 15 minut za účelem provádění výše popsaných úloh:
    • zálohujte protokol
    • určete, na který sekundární server se mají použít nepoužité zálohy protokolů
    • obnovte tyto protokoly s příslušným nastavením
  7. Vytvořte uloženou proceduru (a/nebo pohled?), která volajícím aplikacím řekne, kterou sekundární aplikaci by měly použít pro jakékoli nové dotazy pouze pro čtení.
  8. Vytvořte postup čištění k vymazání historie zálohování protokolů pro protokoly, které byly použity na všechny sekundární soubory (a možná také k přesunutí nebo vymazání samotných souborů).
  9. Rozšiřte řešení o robustní zpracování chyb a upozornění.

Krok 1 – vytvořte databázi

Moje primární instance je Standard Edition s názvem .\BOSS . V tomto případě vytvořím jednoduchou databázi s jednou tabulkou:

POUŽÍVEJTE [master];GOCREATE DATABASE UserData;GOALTER DATABASE OBNOVA SETU UserData FULL;GOUSE UserData;GOCREATE TABLE dbo.LastUpdate(EventTime DATETIME2);INSERT dbo.LastUpdate(EventTime) SELECT SYSDATETIME();

Poté vytvořím úlohu SQL Server Agent, která pouze aktualizuje toto časové razítko každou minutu:

UPDATE UserData.dbo.LastUpdate SET EventTime =SYSDATETIME();

To jen vytvoří počáteční databázi a simuluje aktivitu, což nám umožňuje ověřit, jak se úloha odesílání protokolu střídá přes každou z čitelných sekundárních položek. Chci výslovně uvést, že smyslem tohoto cvičení není zátěžové testování přepravy protokolů nebo dokazování, jak velký objem dokážeme prorazit; to je úplně jiné cvičení.

Krok 2 – přidejte propojené servery

Mám čtyři sekundární instance Express Edition s názvem .\PEON1 , .\PEON2 , .\PEON3 a .\PEON4 . Spustil jsem tedy tento kód čtyřikrát a změnil jsem @s pokaždé:

USE [master];GODECLARE @s NVARCHAR(128) =N'.\PEON1', -- opakujte pro .\PEON2, .\PEON3, .\PEON4 @t NVARCHAR(128) =N'true'; EXEC [master].dbo.sp_addlinkedserver @server =@s, @srvproduct =N'SQL Server';EXEC [master].dbo.sp_addlinkedsrvlogin @rmtsrvname =@s, @useself =@t;EXEC [master].dbo. sp_serveroption @server =@s, @optname =N'colation compatible', @optvalue =@t;EXEC [master].dbo.sp_serveroption @server =@s, @optname =N'data access', @optvalue =@t;EXEC [master].dbo.sp_serveroption @server =@s, @optname =N'rpc', @optvalue =@t;EXEC [master].dbo.sp_serveroption @server =@s, @optname =N'rpc out ', @optvalue =@t;

Krok 3 – ověřte sdílení souborů

V mém případě je všech 5 instancí na stejném serveru, takže jsem pro každou instanci právě vytvořil složku:C:\temp\Peon1\ , C:\temp\Peon2\ , a tak dále. Pamatujte, že pokud jsou vaše sekundární servery na různých serverech, umístění by mělo být relativní k tomuto serveru, ale mělo by být stále přístupné z primárního (takže by se obvykle použila cesta UNC). Měli byste ověřit, že každá instance může zapisovat do této sdílené složky, a také byste měli ověřit, že každá instance může zapisovat do umístění určeného pro soubor v pohotovostním režimu (použil jsem stejné složky pro pohotovostní režim). Můžete to ověřit zálohováním malé databáze z každé instance do každého z jejích určených umístění – nepokračujte, dokud to nebude fungovat.

Krok 4 – vytvoření tabulek

Rozhodl jsem se umístit tato data do msdb , ale ve skutečnosti nemám žádné silné pocity pro nebo proti vytvoření samostatné databáze. První tabulka, kterou potřebuji, je ta, která obsahuje informace o databázích, které budu odesílat:

CREATE TABLE dbo.PMAG_Databases( DatabaseName SYSNAME, LogBackupFrequency_Minutes SMALLINT NOT NULL DEFAULT (15), CONSTRAINT PK_DBS PRIMARY KEY(DatabaseName));GO INSERT dbo.PMAG_Databases
(Pokud jste zvědaví na schéma pojmenování, PMAG je zkratka pro "Skupiny dostupnosti chudáka.")

Další požadovaná tabulka je jedna, která obsahuje informace o sekundárních dílech, včetně jejich jednotlivých složek a jejich aktuálního stavu v sekvenci odesílání protokolu.

CREATE TABLE dbo.PMAG_Secondaries( DatabaseName SYSNAME, ServerInstance SYSNAME, CommonFolder VARCHAR(512) NOT NULL, DataFolder VARCHAR(512) NOT NULL, LogFolder VARCHAR(512) NOT NULL, Is StandByCurrent andLocation BVARIT NOTCHAR NULL DEFAULT 0, CONSTRAINT PK_Sec PRIMARY KEY(DatabaseName, ServerInstance), CONSTRAINT FK_Sec_DBs CIZÍ KLÍČ(DatabaseName) REFERENCE dbo.PMAG_Databases(DatabaseName));

Pokud chcete zálohovat ze zdrojového serveru lokálně a sekundární soubory obnovit vzdáleně nebo naopak, můžete rozdělit CommonFolder do dvou sloupců (BackupFolder a RestoreFolder ) a proveďte příslušné změny v kódu (nebude jich tolik).

Protože mohu tuto tabulku naplnit alespoň částečně na základě informací v sys.servers – využití skutečnosti, že data / log a další složky jsou pojmenovány podle názvů instancí:

INSERT dbo.PMAG_Secondaries( DatabaseName, ServerInstance, CommonFolder, DataFolder, LogFolder, StandByLocation)SELECT DatabaseName =N'UserData', ServerInstance =name, CommonFolder ='C:\temp\Peon' + RIGHT(name, 1) + '\', DataFolder ='C:\Program Files\Microsoft SQL Server\MSSQL12.PEON' + RIGHT(název, 1) + '\MSSQL\DATA\', LogFolder ='C:\Program Files\Microsoft SQL Server\ MSSQL12.PEON' + RIGHT(jméno, 1) + '\MSSQL\DATA\', StandByLocation ='C:\temp\Peon' + RIGHT(jméno, 1) + '\' OD sys.servers KDE jméno LIKE N' .\PEON[1-4]';

Potřebuji také tabulku pro sledování jednotlivých záloh protokolů (nejen poslední), protože v mnoha případech budu muset obnovit více souborů protokolu za sebou. Tyto informace mohu získat z msdb.dbo.backupset , ale je mnohem složitější získat věci, jako je umístění – a možná nebudu mít kontrolu nad jinými úlohami, které mohou čistit historii zálohování.

CREATE TABLE dbo.PMAG_LogBackupHistory( DatabaseName SYSNAME, ServerInstance SYSNAME, BackupSetID INT NOT NULL, Location VARCHAR(2000) NOT NULL, BackupTime DATETIME NOT NULL DEFAULT SYSDATETIMENa(), CONSTRAINT PK_KEY_LBHSup Server FK_LBH_DBs CIZÍ KLÍČ(název databáze) REFERENCE dbo.PMAG_Databases(název databáze), OMEZENÍ FK_LBH_Sec CIZÍ KLÍČ(název databáze, instance serveru) REFERENCE dbo.PMAG_Secondaries(název databáze,
Instance));

Možná si myslíte, že je zbytečné ukládat řádek pro každou sekundární složku a ukládat umístění každé zálohy, ale je to pro budoucí zabezpečení – pro případ, kdy přesouváte CommonFolder pro jakoukoli sekundární složku.

A konečně historie obnovení protokolů, takže kdykoli mohu vidět, které protokoly byly obnoveny a kde, a úloha obnovení si může být jistá, že obnoví pouze protokoly, které ještě nebyly obnoveny:

CREATE TABLE dbo.PMAG_LogRestoreHistory( DatabaseName SYSNAME, ServerInstance SYSNAME, BackupSetID INT, RestoreTime DATETIME, CONSTRAINT PK_LRH PRIMARY KEY(DatabaseName, ServerInstance, BackupSetNabase), CONSTRAINT FK_PMLREIGN_DBs CONSTRAINT FK_PMLREIGN_DBs FORSTRAINT FK_PMLREIGN_DBs CIZÍ KLÍČ(Název databáze, ServerInstance) REFERENCE dbo.PMAG_Secondaries(Název databáze, ServerInstance));

Krok 5 – inicializace sekundárních souborů

Potřebujeme uloženou proceduru, která vygeneruje záložní soubor (a zrcadlí jej do všech umístění požadovaných různými instancemi), a také obnovíme jeden protokol do každého sekundárního, abychom je všechny uvedli do pohotovostního režimu. V tomto okamžiku budou všechny dostupné pro dotazy pouze pro čtení, ale pouze jeden bude v jednu chvíli „aktuální“ pohotovostní režim. Toto je uložená procedura, která bude zpracovávat úplné zálohy a zálohy protokolu transakcí; při požadavku na úplnou zálohu a @init je nastaven na 1, automaticky znovu inicializuje odesílání protokolu.

CREATE PROCEDURE [dbo].[PMAG_Backup] @dbname SYSNAME, @type CHAR(3) ='bak', -- nebo 'trn' @init BIT =0 -- používá se pouze s 'bak'ASBEGIN SET NOCOUNT ON; -- vygenerovat vzor názvu souboru DECLARE @now DATETIME =SYSDATETIME(); DECLARE @fn NVARCHAR(256) =@dbname + N'_' + CONVERT(CHAR(8), @nyní, 112) + RIGHT(REPLICATE('0',6) + CONVERT(VARCHAR(32), DATEDIFF(SECOND) , CONVERT(DATE, @teď), @teď)), 6) + N'.“ + @typ; -- vygenerujte příkaz pro zálohování s MIRROR TO pro každý odlišný CommonFolder DECLARE @sql NVARCHAR(MAX) =N'BACKUP' + CASE @type WHEN 'bak' THEN N' DATABASE ' ELSE N' LOG ' END + QUOTENAME(@dbname) + ' ' + STUFF( (SELECT DISTINCT CHAR(13) + CHAR(10) + N' MIRROR TO DISK =''' + s.CommonFolder + @fn + '''' FROM dbo.PMAG_Secondaries AS s WHERE s.DatabaseName =@dbname FOR XML PATH(''), TYPE).value(N'.[1]',N'nvarchar(max)'),1,9,N'') + N' WITH NAME =N'' ' + @dbname + CASE @type WHEN 'bak' THEN N'_PMAGull' ELSE N'_PMAGLog' END + ''', INIT, FORMAT' + CASE WHEN LEFT(CONVERT(NVARCHAR(128), SERVERPROPERTY(N'Edition') )), 3) IN (N'Dev', N'Ent') THEN N', COMPRESSION;' ELSE N';' KONEC; EXEC [master].sys.sp_executesql @sql; POKUD @type ='bak' AND @init =1 -- inicializovat odeslání protokolu BEGIN EXEC dbo.PMAG_InitializeSecondaries @dbname =@dbname, @fn =@fn; END IF @type ='trn' BEGIN -- zaznamenejte skutečnost, že jsme zálohovali protokol INSERT dbo.PMAG_LogBackupHistory ( DatabaseName, ServerInstance, BackupSetID, Location ) SELECT DatabaseName =@dbname, ServerInstance =s.ServerInstance, BackupSetID =MAX(b .backup_set_id), Location =s.CommonFolder + @fn FROM msdb.dbo.backupset AS b CROSS JOIN dbo.PMAG_Secondaries AS s WHERE b.name =@dbname + N'_PMAGLog' AND s.DatabaseName =@dbname GROUP BY s. ServerInstance, s.CommonFolder + @fn; -- jakmile jsme zazálohovali protokoly, -- obnovte je na dalším sekundárním EXEC dbo.PMAG_RestoreLogs @dbname =@dbname; ENDEND

To zase vyvolá dvě procedury, které byste mohli volat samostatně (ale s největší pravděpodobností ne). Nejprve postup, který inicializuje sekundární soubory při prvním spuštění:

ALTER PROCEDURE dbo.PMAG_InitializeSecondaries @dbname SYSNAME, @fn VARCHAR(512)ASBEGIN SET NOCOUNT ON; -- vymazat existující historii/nastavení (protože to může být re-init) DELETE dbo.PMAG_LogBackupHistory WHERE DatabaseName =@dbname; DELETE dbo.PMAG_LogRestoreHistory WHERE DatabaseName =@dbname; AKTUALIZACE SET dbo.PMAG_Secondaries IsCurrentStandby =0 WHERE DatabaseName =@dbname; DECLARE @sql NVARCHAR(MAX) =N'', @files NVARCHAR(MAX) =N''; - potřebujete znát logická jména souborů - může být více než dva SET @sql =N'SELECT @files =(SELECT N'', MOVE N'''''' + název + '''''' NA N ''''$'' + CASE [type] WHEN 0 THEN N''df'' WHEN 1 THEN N''lf'' END + ''$''''''' FROM ' + QUOTENAME(@dbname) + '.sys.database_files WHERE [type] IN (0,1) FOR XML PATH, TYPE).value(N''.[1]'',N''nvarchar(max)'');'; EXEC master.sys.sp_executesql @sql, N'@files NVARCHAR(MAX) OUTPUT', @files =@files OUTPUT; SET @sql =N''; -- restore - potřeba fyzické cesty datových/logových souborů pro WITH MOVE -- to samozřejmě může selhat, pokud tyto cesty+názvy již existují pro jinou db SELECT @sql +=N'EXEC ' + QUOTENAME(ServerInstance) + N' .master.sys.sp_executesql N''RESTORE DATABASE ' + QUOTENAME(@dbname) + N' Z DISKU =N''''' + CommonFolder + @fn + N''''''' + N' S VÝMĚNOU, BEZ OBNOVENÍ ' + REPLACE(REPLACE(REPLACE(@files, N'$df$', DataFolder + @dbname + N'.mdf'), N'$lf$', LogFolder + @dbname + N'.ldf'), N '''', N'''''') + N';'';' + CHAR(13) + CHAR(10) FROM dbo.PMAG_Secondaries WHERE DatabaseName =@dbname; EXEC [master].sys.sp_executesql @sql; -- záloha protokolu pro tuto databázi EXEC dbo.PMAG_Backup @dbname =@dbname, @type ='trn'; -- obnovit protokoly EXEC dbo.PMAG_RestoreLogs @dbname =@dbname, @PrepareAll =1;END

A pak postup, který obnoví protokoly:

CREATE PROCEDURE dbo.PMAG_RestoreLogs @dbname SYSNAME, @PrepareAll BIT =0ASBEGIN SET NOCOUNT ON; DECLARE @StandbyInstance SYSNAME, @CurrentInstance SYSNAME, @BackupSetID INT, @Location VARCHAR(512), @StandByLocation VARCHAR(512), @sql NVARCHAR(MAX), @rn INT; -- získat "další" pohotovostní instanci SELECT @StandbyInstance =MIN(ServerInstance) FROM dbo.PMAG_Secondaries WHERE IsCurrentStandby =0 AND ServerInstance> (SELECT ServerInstance FROM dbo.PMAG_Secondaries WHERE IsCurrentStandBy =1); IF @StandbyInstance IS NULL -- buď to bylo poslední, nebo re-init BEGIN SELECT @StandbyInstance =MIN(ServerInstance) FROM dbo.PMAG_Secondaries; KONEC -- aktivujte tuto instanci a do STANDBY -- pro každé přihlášení v logbackuphistory, které není v logrestorehistory:-- obnovte a vložte ji do logrestorehistory -- označte poslední jako STANDBY -- pokud je @prepareAll pravdivé, označte všechny ostatní jako NORECOVERY -- v tomto případě by měl být pouze jeden, ale pro případ DECLARE c CURSOR LOCAL FAST_FORWARD FOR SELECT bh.BackupSetID, s.ServerInstance, bh.Location, s.StandbyLocation, rn =ROW_NUMBER() OVER (PARTITION BY s. ServerInstance ORDER BY bh.BackupSetID DESC) FROM dbo.PMAG_LogBackupHistory AS bh INNER JOIN dbo.PMAG_Secondaries AS s ON bh.DatabaseName =s.DatabaseName AND bh.ServerInstance =s.ServerInstanceName @s.ServerInstance WHERE ANDBASE CASE s. @PrepareAll WHEN 1 THEN s.ServerInstance ELSE @StandbyInstance KONEC A NEEXISTUJE ( VYBERTE 1 Z dbo.PMAG_LogRestoreHistory AS rh WHERE DatabaseName =@dbname AND ServerInstance =s.ServerInstance AND BackupSetID =bh.B ackupSetID ) SEŘADIT PO PŘÍPADECH s.ServerInstance WHEN @StandbyInstance THEN 1 ELSE 2 END, bh.BackupSetID; OTEVŘENO c; FETCH c INTO @BackupSetID, @CurrentInstance, @Location, @StandbyLocation, @rn; WHILE @@FETCH_STATUS -1 ZAČÁTEK – vyhoďte uživatele – nastavte na single_user a poté zpět na multi SET @sql =N'EXEC ' + QUOTENAME(@CurrentInstance) + N'.[master].sys.sp_executesql ' + 'N' 'Jestli existuje (SELECT 1 FROM sys.databases WHERE name =N''''' + @dbname + ''''' AND [state] 1) BEGIN ALTER DATABASE ' + QUOTENAME(@dbname) + N' SET SINGLE_USER ' + OKAMŽITÉ VRÁCENÍ N'WITH; ALTER DATABASE ' + QUOTENAME(@dbname) + N' SET MULTI_USER; KONEC;'';'; EXEC [master].sys.sp_executesql @sql; -- obnovte protokol (v STANDBY, pokud je poslední):SET @sql =N'EXEC ' + QUOTENAME(@CurrentInstance) + N'.[master].sys.sp_executesql ' + N'N''RESTORE LOG ' + CITÁTNÍ NÁZEV (@dbname) + N' Z DISKU =N''''' + @Umístění + N''''' WITH ' + CASE WHEN @rn =1 AND (@CurrentInstance =@StandbyInstance OR @PrepareAll =1) THEN N'STANDBY =N''''' + @StandbyLocation + @dbname + N'.standby''''' ELSE N'NORECOVERY' END + N';'';'; EXEC [master].sys.sp_executesql @sql; -- zaznamenejte skutečnost, že jsme obnovili protokoly INSERT dbo.PMAG_LogRestoreHistory (DatabaseName, ServerInstance, BackupSetID, RestoreTime) SELECT @dbname, @CurrentInstance, @BackupSetID, SYSDATETIME(); -- označte nový pohotovostní režim IF @rn =1 AND @CurrentInstance =@StandbyInstance -- toto je nová STANDBY BEGIN UPDATE dbo.PMAG_Secondaries SET IsCurrentStandby =PŘÍPAD ServerInstance WHEN @StandbyInstance THEN 1 ELSE 0 END WHERE DatabaseName =@dbname; END FETCH c INTO @BackupSetID, @CurrentInstance, @Location, @StandbyLocation, @rn; KONEC ZAVŘÍT c; DEALOCATE c;END

(Vím, že je to hodně kódu a hodně kryptického dynamického SQL. Snažil jsem se být velmi liberální v komentářích; pokud máte s nějakým dílem potíže, dejte mi prosím vědět.)

Nyní tedy vše, co musíte udělat, abyste systém uvedli do chodu, je provést dvě volání procedur:

EXEC dbo.PMAG_Backup @dbname =N'UserData', @type ='bak', @init =1;EXEC dbo.PMAG_Backup @dbname =N'UserData', @type ='trn';

Nyní byste měli vidět každou instanci s pohotovostní kopií databáze:

A můžete vidět, který z nich by měl aktuálně sloužit jako pohotovostní režim pouze pro čtení:

SELECT ServerInstance, IsCurrentStandby FROM dbo.PMAG_Secondaries WHERE DatabaseName =N'UserData';

Krok 6 – vytvořte úlohu, která zálohuje/obnovuje protokoly

Tento příkaz můžete vložit do úlohy, kterou naplánujete na každých 15 minut:

EXEC dbo.PMAG_Backup @dbname =N'UserData', @type ='trn';

To posune aktivní sekundár každých 15 minut a jeho data budou o 15 minut aktuálnější než předchozí aktivní sekundár. Pokud máte více databází s různými plány, můžete vytvořit více úloh nebo naplánovat úlohu častěji a zkontrolovat dbo.PMAG_Databases tabulku pro každý jednotlivý LogBackupFrequency_Minutes hodnotu k určení, zda byste měli spustit zálohování/obnovu pro danou databázi.

Krok 7 – zobrazení a postup, jak aplikaci sdělit, který pohotovostní režim je aktivní

CREATE VIEW dbo.PMAG_ActiveSecondariesAS SELECT DatabaseName, ServerInstance FROM dbo.PMAG_Secondaries WHERE IsCurrentStandby =1;PŘEJÍT PROCEDUR VYTVOŘENÍ dbo.PMAG_GetActiveSecondary @dbname SYSNAMEASBEGIN SET NOCOUNT ON; SELECT ServerInstance FROM dbo.PMAG_ActiveSecondaries WHERE DatabaseName =@dbname;ENDGO

V mém případě jsem také ručně vytvořil sjednocení zobrazení napříč všemi UserData databází, abych mohl porovnat aktuálnost dat na primární s každým sekundárním.

CREATE VIEW dbo.PMAG_CompareRecency_UserDataAS WITH x(ServerInstance, EventTime) AS ( SELECT @@SERVERNAME, EventTime FROM UserData.dbo.LastUpdate UNION ALL SELECT N'.\PEON1', EventTime FROM [.\PEON1].UserData.dbo .LastUpdate UNION ALL VYBERTE N'.\PEON2', Čas události OD [.\PEON2].UserData.dbo.LastUpdate UNION VŠE VYBERTE N'.\PEON3', Čas události OD [.\PEON3].UserData.dbo.LastUpdate UNION VŠECHNY SELECT N'.\PEON4', EventTime FROM [.\PEON4].UserData.dbo.LastUpdate ) SELECT x.ServerInstance, s.IsCurrentStandby, x.EventTime, Age_Minutes =DATEDIFF(MINUTE, x.EventTime, SYSDATETIME()), Age_Seconds =DATEDIFF(SECOND, x.EventTime, SYSDATETIME()) OD x LEFT OUTER JOIN dbo.PMAG_Secondaries AS s ON s.ServerInstance =x.ServerInstance AND s.DatabaseName =N'UserData';GO

Ukázkové výsledky z víkendu:

SELECT [Nyní] =SYSDATETIME(); SELECT ServerInstance, IsCurrentStandby, EventTime, Age_Minutes, Age_Seconds FROM dbo.PMAG_CompareRecency_UserData ORDER BY Age_Seconds DESC;

Krok 8 – postup čištění

Vyčištění historie zálohování a obnovení protokolu je docela snadné.

CREATE PROCEDURE dbo.PMAG_CleanupHistory @dbname SYSNAME, @DaysOld INT =7ASBEGIN SET NOCOUNT ON; DECLARE @cutoff INT; -- to předpokládá, že záloha protokolu buď -- byla úspěšná nebo selhala na všech sekundárních položkách SELECT @cutoff =MAX(BackupSetID) FROM dbo.PMAG_LogBackupHistory AS bh WHERE DatabaseName =@dbname AND BackupTime  

Nyní to můžete přidat jako krok ve stávající úloze, nebo to můžete naplánovat zcela samostatně nebo jako součást jiných rutin čištění.

Čištění souborového systému si nechám na jiný příspěvek (a pravděpodobně úplně samostatný mechanismus, jako je PowerShell nebo C# – to obvykle není věc, kterou chcete, aby T-SQL dělal).

Krok 9 – rozšiřte řešení

Je pravda, že by zde mohlo být lepší zpracování chyb a další vychytávky, aby bylo toto řešení úplnější. Pro tuto chvíli to nechám jako cvičení pro čtenáře, ale plánuji se podívat na následné příspěvky, abych podrobně popsala vylepšení a vylepšení tohoto řešení.

Proměnné a omezení

Všimněte si, že v mém případě jsem použil Standard Edition jako primární a Express Edition pro všechny sekundární. Mohli byste jít o krok dále v měřítku rozpočtu a dokonce použít Express Edition jako primární – mnoho lidí si myslí, že Express Edition nepodporuje odesílání protokolů, i když ve skutečnosti je to pouze průvodce, který nebyl přítomen ve verzích Management Studio. Express před SQL Server 2012 Service Pack 1. Vzhledem k tomu, že Express Edition nepodporuje SQL Server Agent, bylo by v tomto scénáři obtížné z ní udělat vydavatele – museli byste nakonfigurovat svůj vlastní plánovač pro volání uložených procedur (C# aplikace příkazového řádku spuštěná plánovačem úloh systému Windows, úlohami PowerShellu nebo úlohami SQL Server Agent na další instanci). Chcete-li použít Express na obou stranách, musíte si být také jisti, že váš datový soubor nepřesáhne 10 GB a vaše dotazy budou fungovat bez problémů s pamětí, procesorem a omezeními funkcí této edice. V žádném případě netvrdím, že Express je ideální; Pouze jsem to použil, abych demonstroval, že je možné mít zdarma (nebo velmi blízko) velmi flexibilní čitelné sekundární stránky.

Všechny tyto samostatné instance v mém scénáři také žijí na stejném virtuálním počítači, ale nemusí to tak vůbec fungovat – instance můžete rozložit na více serverů; nebo můžete jít opačným způsobem a obnovit do různých kopií databáze s různými názvy ve stejné instanci. Tyto konfigurace by vyžadovaly minimální změny toho, co jsem popsal výše. A kolik databází obnovíte a jak často, je zcela na vás – i když bude existovat praktická horní hranice (kde [průměrná doba dotazu]> [počet sekundárních] x [interval zálohování protokolu] ).

Konečně, tento přístup má určitě určitá omezení. Neúplný seznam:

  1. I když můžete pokračovat v provádění úplných záloh podle vlastního plánu, zálohy protokolů musí sloužit jako váš jediný mechanismus zálohování protokolů. Pokud potřebujete ukládat zálohy protokolů pro jiné účely, nebudete moci protokoly zálohovat odděleně od tohoto řešení, protože budou narušovat řetězec protokolů. Místo toho můžete zvážit přidání dalšího MIRROR TO arguments to the existing log backup scripts, if you need to have copies of the logs used elsewhere.
  2. While "Poor Man's Availability Groups" may seem like a clever name, it can also be a bit misleading. This solution certainly lacks many of the HA/DR features of Availability Groups, including failover, automatic page repair, and support in the UI, Extended Events and DMVs. This was only meant to provide the ability for non-Enterprise customers to have an infrastructure that supports multiple readable secondaries.
  3. I tested this on a very isolated VM system with no concurrency. This is not a complete solution and there are likely dozens of ways this code could be made tighter; as a first step, and to focus on the scaffolding and to show you what's possible, I did not build in bulletproof resiliency. You will need to test it at your scale and with your workload to discover your breaking points, and you will also potentially need to deal with transactions over linked servers (always fun) and automating the re-initialization in the event of a disaster.

The "Insurance Policy"

Log shipping also offers a distinct advantage over many other solutions, including Availability Groups, mirroring and replication:a delayed "insurance policy" as I like to call it. At my previous job, I did this with full backups, but you could easily use log shipping to accomplish the same thing:I simply delayed the restores to one of the secondary instances by 24 hours. This way, I was protected from any client "shooting themselves in the foot" going back to yesterday, and I could get to their data easily on the delayed copy, because it was 24 hours behind. (I implemented this the first time a customer ran a delete without a where clause, then called us in a panic, at which point we had to restore their database to a point in time before the delete – which was both tedious and time consuming.) You could easily adapt this solution to treat one of these instances not as a read-only secondary but rather as an insurance policy. More on that perhaps in another post.


  1. přístup k aliasům sloupců v klauzuli where v postgresql

  2. Změňte typ sloupce s čísly z varchar na int

  3. Volání uložené procedury v rámci uložené procedury

  4. SQLite nemůže otevřít soubor databáze (kód 14) při častém dotazu SELECT