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

Vysvětlení vyprázdnění vyrovnávací paměti protokolu

Pravděpodobně jste již mnohokrát slyšeli, že SQL Server poskytuje záruku na vlastnosti transakcí ACID. Tento článek se zaměřuje na část D, která samozřejmě znamená odolnost. Konkrétněji se tento článek zaměřuje na aspekt architektury protokolování serveru SQL Server, který vynucuje trvanlivost transakcí – vyprázdnění vyrovnávací paměti protokolu. Mluvím o funkci, které slouží vyrovnávací paměť protokolu, o podmínkách, které nutí SQL Server vyprázdnit vyrovnávací paměť protokolu na disk, o tom, co můžete udělat pro optimalizaci výkonu transakcí, a také o nedávno přidaných souvisejících technologiích, jako je zpožděná trvanlivost a energetická paměť třídy úložiště.

Proplachování vyrovnávací paměti protokolu

Část D ve vlastnostech transakce ACID znamená trvanlivost. Na logické úrovni to znamená, že když aplikace odešle SQL Server pokyn k potvrzení transakce (explicitně nebo s transakcí s automatickým potvrzením), SQL Server normálně vrátí řízení volajícímu až poté, co může zaručit, že transakce je trvalá. Jinými slovy, jakmile volající získá zpět kontrolu po provedení transakce, může se spolehnout, že i když o chvíli později dojde k výpadku napájení serveru, změny transakce se dostaly do databáze. Pokud se server úspěšně restartuje a databázové soubory nebyly poškozeny, zjistíte, že byly použity všechny změny transakcí.

Trvanlivost transakcí SQL Server částečně vynucuje tak, že zajistí, aby všechny změny transakce byly zapsány do protokolu transakcí databáze na disk. než vrátíte řízení volajícímu. V případě výpadku napájení po potvrzení potvrzení transakce víte, že všechny tyto změny byly alespoň zapsány do transakčního protokolu na disku. To platí i v případě, že související datové stránky byly upraveny pouze v datové mezipaměti (fondu vyrovnávacích pamětí), ale dosud nebyly vyprázdněny do datových souborů na disku. Když restartujete SQL Server, během fáze opakování procesu obnovy SQL Server použije informace zaznamenané v protokolu k přehrání změn, které byly použity po posledním kontrolním bodu a které se nedostaly do datových souborů. V příběhu je toho trochu víc v závislosti na modelu obnovy, který používáte, a na tom, zda byly po posledním kontrolním bodu použity hromadné operace, ale pro účely naší diskuse postačí zaměřit se na část, která zahrnuje posílení změn transakční protokol.

Záludná část architektury protokolování serveru SQL Server spočívá v tom, že zápisy do protokolu jsou sekvenční. Pokud by SQL Server nepoužíval nějaký druh vyrovnávací paměti protokolů ke zmírnění zápisů protokolů na disk, systémy náročné na zápis – zejména ty, které zahrnují spoustu malých transakcí – by se rychle dostaly do strašlivých překážek výkonu souvisejících se zápisem do protokolu.

Ke zmírnění negativního dopadu na výkon častých sekvenčních zápisů protokolu na disk používá SQL Server vyrovnávací paměť protokolu v paměti. Zápisy protokolu se nejprve provádějí do vyrovnávací paměti protokolu a za určitých podmínek SQL Server vyprázdní nebo zpevní vyrovnávací paměť protokolu na disk. Zpevněná jednotka (neboli blok protokolu) se může pohybovat od minimální velikosti sektoru (512 bajtů) po maximálně 60 KB. Níže jsou uvedeny podmínky, které spouštějí vyprázdnění vyrovnávací paměti protokolu (prozatím ignorujte části, které se objevují v hranatých závorkách):

  • SQL Server obdrží požadavek na potvrzení [plně trvalé] transakce, která změní data [v jiné databázi než tempdb]
  • Vyrovnávací paměť protokolu se zaplní a dosáhne své kapacity 60 kB
  • SQL Server potřebuje zpevnit špinavé datové stránky, např. během procesu kontrolního bodu, a záznamy protokolu představující změny na těchto stránkách ještě nebyly zpevněny (protokolování s předstihem , nebo zkráceně WAL)
  • Ručně požádáte o vyprázdnění vyrovnávací paměti protokolu spuštěním procedury sys.sp_flush_log
  • SQL Server zapíše novou hodnotu obnovení související s mezipamětí sekvence [v jiné databázi než tempdb]

První čtyři podmínky by měly být docela jasné, pokud prozatím ignorujete informace v hranatých závorkách. To poslední možná ještě není jasné, ale vysvětlím to podrobně později v článku.

Doba, po kterou SQL Server čeká na dokončení I/O operace zpracovávající vyprázdnění vyrovnávací paměti protokolu, se odráží v typu čekání WRITELOG.

Proč jsou tedy tyto informace tak zajímavé a co s nimi uděláme? Pochopení podmínek, které spouštějí vyprázdnění vyrovnávací paměti protokolu, vám může pomoci zjistit, proč se u určitých úloh vyskytují související úzká místa. V některých případech také existují opatření, která můžete podniknout ke snížení nebo odstranění takových úzkých míst. Uvedu řadu příkladů, jako je jedna velká transakce versus mnoho malých transakcí, plně trvalé versus zpožděné trvalé transakce, uživatelská databáze versus tempdb a ukládání do mezipaměti sekvenčních objektů.

Jedna velká transakce versus mnoho malých transakcí

Jak bylo zmíněno, jednou z podmínek, která spouští vyprázdnění vyrovnávací paměti protokolu, je, když potvrdíte transakci, abyste zaručili trvanlivost transakce. To znamená, že pracovní zátěže, které zahrnují spoustu malých transakcí, jako jsou pracovní zátěže OLTP, mohou potenciálně zaznamenat úzká místa související se zápisem do protokolu.

I když tomu tak často není, pokud během jediné relace odesíláte mnoho malých změn, jednoduchým a efektivním způsobem optimalizace práce je aplikovat změny v jedné velké transakci namísto několika malých.

Zvažte následující zjednodušený příklad (stáhněte si PerformanceV3 zde):

SET NOCOUNT ON; USE PerformanceV3; ALTER DATABASE PerformanceV3 SET DELAYED_DURABILITY =Zakázáno; -- výchozí DROP TABLE IF EXISTS dbo.T1; CREATE TABLE dbo.T1(col1 INT NOT NULL); DECLARE @i AS INT =1; WHILE @i <=1000000ZAČÁTEK ZAČÁTEK PŘENOS INSERT DO dbo.T1(col1) VALUES(@i); COMMIT TRAN; SET @i +=1;END;

Tento kód provede 1 000 000 malých transakcí, které mění data v databázi uživatelů. Tato práce spustí alespoň 1 000 000 vyprázdnění vyrovnávací paměti protokolu. Mohli byste získat několik dalších kvůli zaplnění vyrovnávací paměti protokolů. Pomocí následující testovací šablony můžete spočítat počet vyprázdnění vyrovnávací paměti protokolu a změřit dobu, za kterou byla práce dokončena:

-- Testovací šablona -- ... Příprava je zde ... -- Počítání log flushes a měření časuDECLARE @logflushes AS INT, @starttime AS DATETIME2, @duration AS INT; -- Stats beforeSET @logflushes =( SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name ='Log Flushes/s' AND instance_name =@db ); SET @starttime =SYSDATETIME(); -- ... Sem jde skutečná práce ... -- Statistiky afterSET @duration =DATEDIFF(sekunda, @starttime, SYSDATETIME());SET @logflushes =( SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name ='Proplachy protokolu/sec ' AND název_instance =@db ) - @logflushes; SELECT @duration AS trvání v sekundách, @logflushes AS logflushes;

I když je název čítače výkonu Log Flushes/s, ve skutečnosti stále shromažďuje počet vyprázdnění vyrovnávací paměti protokolu doposud. Kód tedy odečte počet před prací od počtu po práci, aby zjistil počet vyprázdnění protokolu generovaných prací. Tento kód také měří čas v sekundách, který trvalo dokončení práce. I když to zde nedělám, mohli byste, pokud byste chtěli, podobně zjistit počet záznamů protokolu a velikost zapsanou prací do protokolu dotazem na stavy před prací a po práci u fn_dblog funkce.

V našem příkladu výše je část, kterou musíte umístit do přípravné části testovací šablony:

-- PřípravaSET NOCOUNT ON;USE PerformanceV3; ALTER DATABASE PerformanceV3 SET DELAYED_DURABILITY =Zakázáno; DROP TABULKU, POKUD EXISTUJE dbo.T1; CREATE TABLE dbo.T1(col1 INT NOT NULL); DECLARE @db JAKO sysname =N'PerformanceV3'; DECLARE @logflushes JAKO INT, @starttime JAKO DATETIME2, @duration JAKO INT;

A následuje část, kterou musíte umístit do skutečné pracovní sekce:

-- Skutečná práceDECLARE @i AS INT =1; WHILE @i <=1000000ZAČÁTEK ZAČÁTEK PŘENOS INSERT DO dbo.T1(col1) VALUES(@i); COMMIT TRAN; SET @i +=1;END;

Dohromady získáte následující kód:

-- Příklad testu s mnoha malými plně trvanlivými transakcemi v uživatelské databázi-- ... Příprava zde ... -- PřípravaSET NOCOUNT ON;USE PerformanceV3; ALTER DATABASE PerformanceV3 SET DELAYED_DURABILITY =Zakázáno; DROP TABULKU, POKUD EXISTUJE dbo.T1; CREATE TABLE dbo.T1(col1 INT NOT NULL); DECLARE @db JAKO sysname =N'PerformanceV3'; DECLARE @logflushes JAKO INT, @starttime JAKO DATETIME2, @duration JAKO INT; -- Stats beforeSET @logflushes =( SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name ='Log Flushes/s' AND instance_name =@db ); SET @starttime =SYSDATETIME(); -- ... Skutečná práce jde sem ... -- Skutečná práce DECLARE @i AS INT =1; WHILE @i <=1000000ZAČÁTEK ZAČÁTEK PŘENOS INSERT DO dbo.T1(col1) VALUES(@i); COMMIT TRAN; SET @i +=1;KONEC; -- Statistika afterSET @duration =DATEDIFF(sekunda, @počáteční čas, SYSDATETIME()); SET @logflushes =( SELECT cntr_value FROM sys.dm_os_performance_counters WHERE counter_name ='Log Flushes/s' AND instance_name =@db ) - @logflushes; SELECT @duration AS trvání v sekundách, @logflushes AS logflushes;

Dokončení tohoto kódu v mém systému trvalo 193 sekund a spustilo 1 000 036 vyprázdnění vyrovnávací paměti protokolu. To je velmi pomalé, ale lze to vysvětlit velkým počtem vyprázdnění protokolu.

V typických pracovních zátěžích OLTP různé relace odesílají malé změny v různých malých transakcích současně, takže to není tak, že byste skutečně měli možnost zapouzdřit spoustu malých změn do jedné velké transakce. Pokud je však vaše situace taková, že všechny malé změny jsou odeslány ze stejné relace, jednoduchým způsobem optimalizace práce je zapouzdřit ji do jediné transakce. Tím získáte dvě hlavní výhody. Jedním z nich je, že vaše práce bude zapisovat méně záznamů protokolu. Při 1 000 000 malých transakcích každá transakce ve skutečnosti zapisuje tři záznamy protokolu:jeden pro zahájení transakce, jeden pro změnu a jeden pro potvrzení transakce. Takže se díváte na přibližně 3 000 000 záznamů protokolu transakcí oproti 1 000 000, když jsou provedeny jako jedna velká transakce. Ale co je důležitější, u jedné velké transakce se většina vyprázdnění protokolu spustí pouze tehdy, když se zaplní vyrovnávací paměť protokolu, plus jedno vyprázdnění protokolu na samém konci transakce, když se potvrdí. Rozdíl ve výkonu může být poměrně významný. Chcete-li otestovat práci v jedné velké transakci, použijte následující kód ve skutečné pracovní části testovací šablony:

-- Skutečná práceBEGIN TRAN; DECLARE @i AS INT =1; WHILE @i <=1000000BEGIN INSERT INTO dbo.T1(col1) VALUES(@i); SET @i +=1; KONEC; COMMIT TRAN;

V mém systému byla tato práce dokončena za 7 sekund a spustila 1 758 vyprázdnění protokolu. Zde je srovnání těchto dvou možností:

#transactions log trvání vyprázdnění v sekundách-------------- ------------ -------------- ------1000000 1000036 1931 1758 7

Ale znovu, v typickém pracovním zatížení OLTP nemáte ve skutečnosti možnost nahradit mnoho malých transakcí odeslaných z různých relací jednou velkou transakcí odeslanou ze stejné relace.

Plně trvanlivé versus zpožděné trvanlivé transakce

Počínaje SQL Serverem 2014 můžete použít funkci zvanou zpožděná trvanlivost, která vám umožní zlepšit výkon úloh s mnoha malými transakcemi, i když jsou odeslány různými relacemi, a to obětováním běžné záruky plné trvanlivosti. Při potvrzení opožděné trvalé transakce SQL Server potvrdí potvrzení, jakmile je záznam protokolu potvrzení zapsán do vyrovnávací paměti protokolu, aniž by se spustilo vyprázdnění vyrovnávací paměti protokolu. Vyrovnávací paměť protokolu se vyprázdní kvůli kterékoli z výše uvedených podmínek, například když se zaplní, ale ne při potvrzení opožděné trvalé transakce.

Před použitím této funkce si musíte velmi dobře rozmyslet, zda je pro vás vhodná. Z hlediska výkonu je jeho dopad významný pouze při pracovní zátěži se spoustou malých transakcí. Pokud vaše pracovní vytížení nejprve zahrnuje velké transakce, pravděpodobně neuvidíte žádnou výkonnostní výhodu. Ještě důležitější je, že si musíte uvědomit potenciál ztráty dat. Řekněme, že aplikace provede zpožděnou trvalou transakci. Záznam potvrzení je zapsán do vyrovnávací paměti protokolu a okamžitě potvrzen (kontrola je předána zpět volajícímu). Pokud SQL Server dojde k výpadku napájení před vyprázdněním vyrovnávací paměti protokolu, po restartu proces obnovy vrátí zpět všechny změny provedené transakcí, i když si aplikace myslí, že byla potvrzena.

Kdy je tedy vhodné tuto funkci používat? Jeden zřejmý případ je, když ztráta dat nepředstavuje problém, jako je tento příklad od SentryOne Melissa Connors. Další je, když po restartu máte prostředky k identifikaci, které změny se do databáze nedostaly, a jste schopni je reprodukovat. Pokud vaše situace nespadá do jedné z těchto dvou kategorií, nepoužívejte tuto funkci navzdory pokušení.

Chcete-li pracovat se zpožděnými trvalými transakcemi, musíte nastavit volbu databáze s názvem DELAYED_DURABILITY. Tuto možnost lze nastavit na jednu ze tří hodnot:

  • Zakázáno (výchozí):všechny transakce v databázi jsou plně trvalé, a proto každé potvrzení spouští vyprázdnění vyrovnávací paměti protokolu
  • Vynuceno :všechny transakce v databázi jsou zpožděné a trvalé, a proto potvrzení nespouští vyprázdnění vyrovnávací paměti protokolu
  • Povoleno :pokud není uvedeno jinak, transakce jsou plně trvalé a jejich potvrzení spouští vyprázdnění vyrovnávací paměti protokolu; pokud však použijete volbu DELAYED_DURABILITY =ON buď v příkazu COMMIT TRAN, nebo atomickém bloku (nativně zkompilovaného proc), daná konkrétní transakce je zpožděná a trvá, a proto její potvrzení nespustí vyprázdnění vyrovnávací paměti protokolu

Jako test použijte následující kód v přípravné části naší testovací šablony (všimněte si, že možnost databáze je nastavena na Forced):

-- PřípravaSET NOCOUNT ON;USE PerformanceV3; -- http://tsql.solidq.com/SampleDatabases/PerformanceV3.zip ALTER DATABASE PerformanceV3 SET DELAYED_DURABILITY =Vynuceno; DROP TABULKU, POKUD EXISTUJE dbo.T1; CREATE TABLE dbo.T1(col1 INT NOT NULL); DECLARE @db JAKO sysname =N'PerformanceV3';

A použijte následující kód v sekci skutečné práce (upozornění, 1 000 000 malých transakcí):

-- Skutečná práceDECLARE @i AS INT =1; WHILE @i <=1000000ZAČÁTEK ZAČÁTEK PŘENOS INSERT DO dbo.T1(col1) VALUES(@i); COMMIT TRAN; SET @i +=1;END;

Případně můžete použít povolený režim na úrovni databáze a poté v příkazu COMMIT TRAN přidat WITH (DELAYED_DURABILITY =ON).

V mém systému trvalo dokončení práce 22 sekund a spustilo 95 407 vyprázdnění protokolu. To je déle než spuštění práce jako jedné velké transakce (7 sekund), protože se generuje více záznamů protokolu (pamatujte, na transakci, jeden pro zahájení transakce, jeden pro změnu a jeden pro potvrzení transakce); je to však mnohem rychlejší než 193 sekund, které trvalo dokončení práce pomocí 1 000 000 plně trvanlivých transakcí, protože počet vyprázdnění protokolu klesl z více než 1 000 000 na méně než 100 000. Navíc se zpožděnou trvanlivostí získáte nárůst výkonu, i když jsou transakce odesílány z různých relací, kde není možné použít jednu velkou transakci.

Chcete-li demonstrovat, že použití opožděné trvanlivosti při provádění práce jako velké transakce nemá žádný přínos, ponechte stejný kód v přípravné části posledního testu a ve skutečné pracovní části použijte následující kód:

-- Skutečná práceBEGIN TRAN; DECLARE @i AS INT =1; WHILE @i <=1000000BEGIN INSERT INTO dbo.T1(col1) VALUES(@i); SET @i +=1;KONEC; COMMIT TRAN;

Získal jsem 8 sekund běhu (ve srovnání se 7 pro jednu velkou plně trvanlivou transakci) a 1 759 vyprázdnění logu (ve srovnání s 1 758). Čísla jsou v podstatě stejná, ale se zpožděnou trvalou transakcí máte riziko ztráty dat.

Zde je souhrn hodnot výkonu pro všechny čtyři testy:

trvanlivost #transactions log trvání vyprázdnění v sekundách------------------- -------------- ------ ------ --------------------plný 1000000 1000036 193plný 1 1758 7zpožděný 1000000 95407 22zpožděný 11759 8

Paměť třídy úložiště

Funkce zpožděné trvanlivosti může výrazně zlepšit výkon úloh ve stylu OLTP, které zahrnují velký počet malých aktualizačních transakcí, které vyžadují vysokou frekvenci a nízkou latenci. Problém je v tom, že riskujete ztrátu dat. Co když nemůžete připustit žádnou ztrátu dat, ale přesto chcete zvýšení výkonu podobné zpožděné trvanlivosti, kde se vyrovnávací paměť protokolu nevyprázdní při každém potvrzení, spíše když se zaplní? Všichni rádi jíme dort a máme ho také, že?

Toho můžete dosáhnout v SQL Server 2016 SP1 nebo novějším pomocí paměti třídy úložiště, neboli energeticky nezávislého úložiště NVDIMM-N. Tento hardware je v podstatě paměťový modul, který vám poskytuje výkon na úrovni paměti, ale informace v něm zůstávají zachovány, a proto se neztratí ani po výpadku napájení. Přidání v SQL Server 2016 SP1 umožňuje nakonfigurovat vyrovnávací paměť protokolu jako trvalou na takovém hardwaru. Chcete-li to provést, nastavte SCM jako svazek v systému Windows a naformátujte jej jako svazek DAX (Direct Access Mode). Potom přidáte soubor protokolu do databáze pomocí normálního příkazu ALTER DATABASE ADD LOG FILE s cestou k souboru umístěnou na svazku DAX a nastavíte velikost na 20 MB. SQL Server zase rozpozná, že se jedná o svazek DAX, a od tohoto okamžiku považuje vyrovnávací paměť protokolu za trvalou na tomto svazku. Události potvrzení transakce již nespouštějí vyprázdnění vyrovnávací paměti protokolu, ale jakmile bylo potvrzení zaznamenáno do vyrovnávací paměti protokolu, SQL Server ví, že ve skutečnosti přetrvává, a proto vrátí řízení volajícímu. Když se vyrovnávací paměť protokolu zaplní, SQL Server jej vyprázdní do souborů protokolu transakcí na tradičním úložišti.

Další podrobnosti o této funkci, včetně čísel o výkonu, najdete v tématu Zrychlení latence potvrzení transakce pomocí paměti třídy úložiště v systému Windows Server 2016/SQL Server 2016 SP1 od Kevina Farleeho.

Je zajímavé, že SQL Server 2019 vylepšuje podporu pro paměť třídy úložiště nad rámec scénáře trvalé mezipaměti protokolu. Podporuje umístění datových souborů, souborů protokolu a souborů kontrolních bodů OLTP v paměti na takový hardware. Vše, co musíte udělat, je vystavit jej jako svazek na úrovni operačního systému a naformátovat jako jednotku DAX. SQL Server 2019 automaticky rozpozná tuto technologii a funguje osvíceně režim, přímý přístup k zařízení a obcházení zásobníku operačního systému. Vítejte v budoucnosti!

Uživatelská databáze versus tempdb

Databáze tempdb je samozřejmě vytvořena od začátku jako nová kopie modelové databáze pokaždé, když restartujete SQL Server. Proto není nikdy potřeba obnovovat žádná data, která zapisujete do databáze tempdb, ať už je zapisujete do dočasných tabulek, proměnných tabulek nebo uživatelských tabulek. Po restartu je vše pryč. S vědomím toho může SQL Server zmírnit mnoho požadavků souvisejících s protokolováním. Například, bez ohledu na to, zda povolíte volbu odložené trvanlivosti nebo ne, události potvrzení nespustí vyprázdnění vyrovnávací paměti protokolu. Kromě toho se sníží množství informací, které je třeba protokolovat, protože SQL Server potřebuje pouze dostatek informací pro podporu vrácení transakcí nebo vrácení práce, je-li to potřeba, ale ne rolování transakcí vpřed nebo opakování práce. Výsledkem je, že záznamy protokolu transakcí představující změny objektu v databázi tempdb mají tendenci být menší ve srovnání s případy, kdy je stejná změna aplikována na objekt v databázi uživatelů.

Chcete-li to demonstrovat, spustíte stejné testy, které jste spustili dříve v PerformanceV3, pouze tentokrát v databázi tempdb. Začneme testem mnoha malých transakcí, když je možnost databáze DELAYED_DURABILITY nastavena na Disabled (výchozí). V části přípravy testovací šablony použijte následující kód:

-- PřípravaSET NOCOUNT ON;USE tempdb; ALTER DATABASE tempdb SET DELAYED_DURABILITY =Zakázáno; DROP TABULKU, POKUD EXISTUJE dbo.T1; CREATE TABLE dbo.T1(col1 INT NOT NULL); DECLARE @db JAKO sysname =N'tempdb';

V sekci skutečné práce použijte následující kód:

-- Skutečná práceDECLARE @i AS INT =1; WHILE @i <=1000000ZAČÁTEK ZAČÁTEK PŘENOS INSERT DO dbo.T1(col1) VALUES(@i); COMMIT TRAN; SET @i +=1;END;

Tato práce vygenerovala 5 095 vyprázdnění protokolu a její dokončení trvalo 19 sekund. To je ve srovnání s více než milionem vyprázdnění protokolu a 193 sekundami v uživatelské databázi s plnou životností. To je ještě lepší než se zpožděnou trvanlivostí v uživatelské databázi (95 407 vyprázdnění protokolu a 22 sekund) kvůli menší velikosti záznamů protokolu.

Chcete-li otestovat jednu velkou transakci, ponechte sekci přípravy nezměněnou a v sekci skutečné práce použijte následující kód:

-- Skutečná práceBEGIN TRAN; DECLARE @i AS INT =1; WHILE @i <=1000000BEGIN INSERT INTO dbo.T1(col1) VALUES(@i); SET @i +=1;KONEC; COMMIT TRAN;

Mám 1 228 log flushů a 9 sekund běhu. To je ve srovnání s 1 758 vyprázdněními protokolu a 7 sekundami běhu v databázi uživatelů. Doba běhu je podobná, dokonce o něco rychlejší v databázi uživatelů, ale mezi testy mohou být malé odchylky. Velikosti záznamů protokolu v databázi tempdb jsou zmenšeny, a proto získáte méně vyprázdnění protokolu ve srovnání s databází uživatelů.

Můžete také zkusit spustit testy s volbou DELAYED_DURABILITY nastavenou na Forced, ale to nebude mít žádný dopad na tempdb, protože, jak bylo zmíněno, události potvrzení každopádně nespouštějí vyprázdnění protokolu v tempdb.

Zde jsou míry výkonu pro všechny testy, jak v databázi uživatelů, tak v databázi tempdb:

trvanlivost databáze #transactions log trvání vyprázdnění v sekundách-------------- ------------------ ----- --------- ------------ --------------------VýkonV3 plný 1000000 1000036 193VýkonV3 plný 1 1758 7VýkonV3 zpožděný 1000000 95407 22Výkon V3 zpožděný 1 1759 8tempdb plný 1000000 5095 19tempdb plný 1 1228 9tempdb zpožděný 1000000 5091 18tempdb zpožděný 19

Ukládání objektů do mezipaměti sekvencí

Možná překvapivý případ, který spouští vyprázdnění vyrovnávací paměti protokolu, souvisí s volbou mezipaměti sekvenčních objektů. Uvažujme jako příklad následující definici sekvence:

CREATE SEQUENCE dbo.Seq1 AS BIGINT MINVALUE 1 CACHE 50; -- výchozí velikost mezipaměti je 50;

Pokaždé, když potřebujete novou hodnotu sekvence, použijete funkci NEXT VALUE FOR, například:

VYBERTE DALŠÍ HODNOTU PRO dbo.Seq1;

Vlastnost CACHE je funkcí výkonu. Bez něj by při každém požadavku na novou hodnotu sekvence musel SQL Server zapsat aktuální hodnotu na disk pro účely obnovení. To je skutečně chování, které získáte při použití režimu NO CACHE. Místo toho, když je možnost nastavena na hodnotu větší než nula, SQL Server zapíše hodnotu obnovení na disk pouze jednou za každý počet požadavků velikosti mezipaměti. SQL Server udržuje v paměti dva členy, jejichž velikost odpovídá typu sekvence, jeden s aktuální hodnotou a jeden s počtem hodnot zbývajících před dalším zápisem hodnoty obnovení na disk. V případě výpadku napájení po restartu SQL Server nastaví aktuální hodnotu sekvence na hodnotu obnovy.

To je pravděpodobně mnohem jednodušší vysvětlit na příkladu. Zvažte výše uvedenou definici sekvence s volbou CACHE nastavenou na 50 (výchozí). Poprvé požadujete novou hodnotu sekvence spuštěním výše uvedeného příkazu SELECT. SQL Server nastaví výše uvedené členy na následující hodnoty:

Hodnota obnovení na disku:50, Aktuální hodnota v paměti:1, Zbývající hodnoty v paměti:49, Získáte:1

49 dalších požadavků se nedotkne disku, ale pouze aktualizuje paměťové členy. Po 50 žádostech celkem jsou členové nastaveni na následující hodnoty:

Hodnota obnovení na disku:50, Aktuální hodnota v paměti:50, Zbývající hodnoty v paměti:0, Získáte:50

Proveďte další požadavek na novou hodnotu sekvence a to spustí zápis na disk s hodnotou obnovení 100. Členové jsou pak nastaveni na následující hodnoty:

Hodnota obnovení na disku:100, Aktuální hodnota v paměti:51, Zbývající hodnoty v paměti:49, Získáte:51

Pokud v tomto okamžiku dojde k výpadku napájení systému, po restartu se aktuální hodnota sekvence nastaví na 100 (hodnota obnovená z disku). Další požadavek na hodnotu sekvence vytvoří 101 (zapsání hodnoty obnovy 150 na disk). Ztratili jste všechny hodnoty v rozsahu 52 až 100. Nejvíce můžete ztratit kvůli nečistému ukončení procesu SQL Serveru, jako v případě výpadku napájení, tolik hodnot, jako je velikost mezipaměti. Kompromis je jasný; čím větší velikost mezipaměti, tím méně zápisů hodnoty obnovy na disk, a tedy lepší výkon. Současně platí, že čím větší je mezera, která může být generována mezi dvěma sekvenčními hodnotami v případě výpadku napájení.

To vše je docela jednoduché a možná jste velmi dobře obeznámeni s tím, jak to funguje. Co může být překvapivé, je, že pokaždé, když SQL Server zapíše na disk novou hodnotu obnovy (v našem příkladu každých 50 požadavků), také zpevní vyrovnávací paměť protokolu. To není případ vlastnosti sloupce identity, i když SQL Server interně používá stejnou funkci ukládání do mezipaměti pro identitu jako pro objekt sekvence, prostě vám to nedovolí řídit jeho velikost. Ve výchozím nastavení je zapnutá s velikostí 10000 pro BIGINT a NUMERIC, 1000 pro INT, 100 pro SMALLINT a 10 PRO TINYINT. Pokud chcete, můžete jej vypnout příznakem trasování 272 nebo možností konfigurace s rozsahem IDENTITY_CACHE (2017+). Důvod, proč SQL Server nemusí vyprázdnit vyrovnávací paměť protokolu při zápisu hodnoty obnovení související s mezipamětí identity na disk, je ten, že novou hodnotu identity lze vytvořit pouze při vkládání řádku do tabulky. V případě výpadku napájení bude řádek vložený do tabulky transakcí, která nebyla potvrzena, vytažen z tabulky v rámci procesu obnovy databáze při restartu systému. Takže i když po restartu SQL Server vygeneruje stejnou hodnotu identity jako ta, která byla vytvořena v transakci, která nebyla potvrzena, neexistuje žádná šance na duplikáty, protože řádek byl vytažen z tabulky. Pokud by byla transakce potvrzena, spustilo by to vyprázdnění protokolu, které by také přetrvávalo v zápisu hodnoty obnovení související s mezipamětí. Microsoft se tedy necítil nucen vyprázdnit vyrovnávací paměť protokolu pokaždé, když dojde k zápisu hodnoty obnovení na disk související s mezipamětí identity.

U objektu sekvence je situace jiná. Aplikace si může vyžádat novou sekvenční hodnotu a neukládat ji do databáze. V případě výpadku napájení po vytvoření nové sekvenční hodnoty v transakci, která se nepotvrdila, po restartu SQL Server nemůže aplikaci sdělit, aby na tuto hodnotu nespoléhala. Proto, aby se po restartu nevytvářela nová hodnota sekvence, která se rovná dříve vygenerované hodnotě sekvence, SQL Server vynutí vyprázdnění protokolu pokaždé, když je na disk zapsána nová hodnota obnovení související s mezipamětí sekvence. Jednou výjimkou z tohoto pravidla je, když je objekt sekvence vytvořen v tempdb, samozřejmě není potřeba takové vyprázdnění protokolu, protože po restartu systému je tempdb vytvořena znovu.

Negativní dopad na výkon častého vyprázdnění protokolu je zvláště patrný při použití velmi malé velikosti mezipaměti sekvencí a při jedné transakci generující mnoho sekvenční hodnoty, např. při vkládání mnoha řádků do tabulky. Bez sekvence by taková transakce většinou ztvrdla vyrovnávací paměť protokolu, když se zaplní, plus ještě jednou, když se transakce potvrdí. Ale s touto sekvencí získáte vyprázdnění protokolu pokaždé, když dojde k zápisu hodnoty obnovy na disk. To je důvod, proč se chcete vyhnout použití malé velikosti mezipaměti – nemluvě o režimu NO CACHE.

Chcete-li to demonstrovat, použijte následující kód v části přípravy naší testovací šablony:

-- PřípravaSET NOCOUNT ON;USE PerformanceV3; -- zkuste PerformanceV3, tempdb ALTER DATABASE PerformanceV3 -- zkuste PerformanceV3, tempdb SET DELAYED_DURABILITY =Zakázáno; -- zkuste Zakázáno, Vynuceno DROP TABLE IF EXISTS dbo.T1; POKUD EXISTUJE DROP SEKVENCE dbo.Seq1; CREATE SEQUENCE dbo.Seq1 AS BIGINT MINVALUE 1 CACHE 50; -- zkuste NO CACHE, CACHE 50, CACHE 10000 DECLARE @db AS sysname =N'PerformanceV3'; -- zkuste PerformanceV3, tempdb

A následující kód v sekci skutečné práce:

-- Aktuální workSELECT -- n -- pro testování bez seq NEXT VALUE FOR dbo.Seq1 AS n -- pro testování sekvenceINTO dbo.T1FROM PerformanceV3.dbo.GetNums(1, 1000000) AS N;

Tento kód používá jednu transakci k zápisu 1 000 000 řádků do tabulky pomocí příkazu SELECT INTO, čímž se vygeneruje tolik sekvenčních hodnot, kolik je vložených řádků.

Podle pokynů v komentářích spusťte test s NO CACHE, CACHE 50 a CACHE 10000, a to jak v PerformanceV3, tak v tempdb, a vyzkoušejte plně trvalé transakce i zpožděné trvalé transakce.

Zde jsou čísla výkonu, která jsem na svém systému získal:

doba trvání vyprázdnění protokolu mezipaměti trvanlivosti databáze v sekundách-------------- ------------------- ------ --- ------------ -------------------- Výkon V3 plný ŽÁDNÁ CACHE 1000047 171VýkonV3 plný 50 20008 4VýkonV3 plný 10 000 339 <1tempdb plná NO CACHE 96 4tempdb plná 50 74 1tempdb plná 10000 8 <1PerformanceV3 zpožděná NO CACHE 1000045 166PerformanceV3 zpožděná 50 20011 4PerformanceV3 zpožděná 10000 CHEdb zpožděná 10000 temp 334 zpožděná 004 NO temp4 334 

Je zde několik zajímavých věcí, kterých si musíte všimnout.

S NO CACHE získáte log flush pro každou jednotlivou vygenerovanou sekvenční hodnotu. Proto důrazně doporučujeme se tomu vyhnout.

S malou velikostí mezipaměti sekvence stále získáváte spoustu vyprázdnění protokolu. Možná situace není tak špatná jako u NO CACHE, ale všimněte si, že dokončení úlohy trvalo 4 sekundy s výchozí velikostí mezipaměti 50 ve srovnání s méně než sekundou s velikostí 10 000. Osobně používám 10 000 jako preferovanou hodnotu.

In tempdb you don’t get log flushes when a sequence cache-related recovery value is written to disk, but the recovery value is still written to disk every cache-sized number of requests. That’s perhaps surprising since such a value would never need to be recovered. Therefore, even when using a sequence object in tempdb, I’d still recommend using a large cache size.

Also notice that delayed durability doesn’t prevent the need for log flushes every time the sequence cache-related recovery value is written to disk.

Závěr

This article focused on log buffer flushes. Understanding this aspect of SQL Server’s logging architecture is important especially in order to be able to optimize OLTP-style workloads that require high frequency and low latency. Workloads using In-Memory OLTP included, of course. You have more options with newer features like delayed durability and persisted log buffer with storage class memory. Make sure you’re very careful with the former, though, since it does incur potential for data loss unlike the latter.

Be careful not to use the sequence object with a small cache size, not to speak of the NO CACHE mode. I find the default size 50 too small and prefer to use 10,000. I’ve heard people expressing concerns that with a cache size 10000, after multiple power failures they might lose all the values in the type. However, even with a four-byte INT type, using only the positive range, 10,000 fits 214,748 times. If your system experience that many power failures, you have a completely different problem to worry about. Therefore, I feel very comfortable with a cache size of 10,000.


  1. Nejste to vy, to jsem já (odstraňování problémů s I/O)

  2. Jak změnit barvu pozadí formuláře v Accessu 2016

  3. Aktualizovat tabulku pomocí JOIN na SQL Server?

  4. Jak pretify výstup pocházející z dotazu SELECT v příkazovém řádku?