Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
platí pro:SQL Server
Azure SQL Database
azure SQL Managed Instance
Tabulky optimalizované pro paměť vyžadují, aby existoval dostatek paměti k uchování všech řádků a indexů. Vzhledem k tomu, že paměť je konečný prostředek, je důležité pochopit a spravovat využití paměti ve vašem systému. Témata v této části se týkají běžných scénářů použití a správy paměti.
Je důležité mít rozumný odhad potřeb paměti každé tabulky optimalizované pro paměť, abyste mohli server zřídit s dostatečnou pamětí. To platí pro nové tabulky i tabulky migrované z diskových tabulek. Tato část popisuje, jak odhadnout velikost paměti, kterou potřebujete uchovávat data pro tabulku optimalizovanou pro paměť.
Pokud uvažujete o migraci z tabulek založených na disku na tabulky optimalizované pro paměť, přečtěte si téma Určení, jestli by se měla přenést tabulka nebo uložená procedura do In-Memory OLTP , kde najdete pokyny, které tabulky se mají nejlépe migrovat. Všechna témata v části Migrace na In-Memory OLTP poskytují pokyny k migraci z diskových do tabulek optimalizovaných pro paměť.
Základní pokyny pro odhad požadavků na paměť
V SQL Serveru 2016 (13.x) a novějších verzích není nijak omezena velikost tabulek optimalizovaných pro paměť, i když se tabulky musí vejít do paměti. V SQL Serveru 2014 (12.x) je podporovaná velikost dat pro tabulky SCHEMA_AND_DATA 256 GB.
Velikost tabulky optimalizované pro paměť odpovídá velikosti dat a režijním nákladům na záhlaví řádků. Velikost tabulky optimalizované pro paměť přibližně odpovídá velikosti clusterovaného indexu nebo haldy původní tabulky založené na disku.
Indexy v tabulkách optimalizovaných pro paměť mají tendenci být menší než neclusterované indexy v tabulkách založených na disku. Velikost neklastrovaných indexů je v řádu [primary key size] * [row count]. Velikost hash indexů je [bucket count] * 8 bytes.
Pokud je aktivní úloha, je potřeba další paměť, aby se zohlednila správa verzí řádků a různé operace. Požadované množství paměti závisí na úloze, ale aby bylo doporučení bezpečné, je začít s dvojnásobnou očekávanou velikostí tabulek a indexů optimalizovaných pro paměť a sledovat skutečnou spotřebu paměti. Režie pro správu verzí řádků vždy závisí na vlastnostech úlohy – zejména dlouhotrvající transakce zvyšují režii. U většiny úloh používajících větší databáze (například větší než 100 GB) je režie omezená (25 procent nebo méně).
Další informace o potenciální režii paměti v modulu In-Memory OLTP naleznete v tématu Fragmentace paměti.
Podrobný výpočet požadavků na paměť
Příklad tabulky optimalizované pro paměť
Zvažte následující schéma tabulky optimalizované pro paměť:
CREATE TABLE t_hk
(
col1 int NOT NULL PRIMARY KEY NONCLUSTERED,
col2 int NOT NULL INDEX t1c2_index
HASH WITH (bucket_count = 5000000),
col3 int NOT NULL INDEX t1c3_index
HASH WITH (bucket_count = 5000000),
col4 int NOT NULL INDEX t1c4_index
HASH WITH (bucket_count = 5000000),
col5 int NOT NULL INDEX t1c5_index NONCLUSTERED,
col6 char (50) NOT NULL,
col7 char (50) NOT NULL,
col8 char (30) NOT NULL,
col9 char (50) NOT NULL
) WITH (memory_optimized = on) ;
GO
Pomocí tohoto schématu určíme minimální paměť potřebnou pro tuto tabulku optimalizovanou pro paměť.
Paměť pro tabulku
Řádek tabulky optimalizovaný pro paměť má tři části:
Časová razítka
Záhlaví řádku a časová razítka = 24 bajtů.ukazatele indexu
Pro každý hashový index v tabulce má každý řádek 8-bajtový ukazatel adresy na další řádek v indexu. Vzhledem k tomu, že existují čtyři indexy, každý řádek přidělí 32 bajtů pro ukazatele indexu (ukazatel 8 bajtů pro každý index).data
Velikost datové části řádku je určena součtem velikosti typu pro každý sloupec dat. V naší tabulce máme pět 4bajtů celých čísel, tři 50 bajtové sloupce znaků a jeden 30 bajtový sloupec znaků. Datová část každého řádku je tedy 4 + 4 + 4 + 4 + 4 + 50 + 50 + 30 + 50 nebo 200 bajtů.
Následuje výpočet velikosti 5 000 000 (5 milionů) řádků v tabulce optimalizované pro paměť. Celková paměť používaná datovými řádky se odhaduje takto:
Paměť pro řádky tabulky
Z výše uvedených výpočtů je velikost každého řádku v tabulce optimalizované pro paměť 24 + 32 + 200 nebo 256 bajtů. Vzhledem k tomu, že máme 5 milionů řádků, tabulka spotřebuje 5 000 000 × 256 bajtů nebo 1 280 000 000 bajtů – přibližně 1,28 GB.
Paměť pro indexy
Paměť pro každý hash index
Každý index hash je pole hash s 8 bajtovými ukazateli adres. Velikost pole je nejlépe určena počtem jedinečných hodnot indexu pro daný index. V aktuálním příkladu je počet jedinečných hodnot col2 dobrým výchozím bodem pro velikost pole pro t1c2_index. Pole hash, které je příliš velké, ztrácí paměť. Hashové pole, které je příliš malé, způsobuje snížení výkonu, protože příliš mnoho hodnot hashují na stejný index, což vede k příliš mnoha kolizím.
Hashovací indexy umožňují velmi rychlé vyhledávání podle rovnosti, například:
SELECT * FROM t_hk
WHERE Col2 = 3;
Neclusterované indexy jsou pro vyhledávání rozsahů rychlejší, například:
SELECT * FROM t_hk
WHERE Col2 >= 3;
Pokud migrujete tabulku založenou na disku, můžete pomocí následujícího příkazu určit počet jedinečných hodnot pro t1c2_index indexu.
SELECT COUNT(DISTINCT [Col2])
FROM t_hk;
Pokud vytváříte novou tabulku, musíte před nasazením odhadnout velikost pole nebo shromáždit data z testování.
Informace o tom, jak fungují hash indexy v In-Memory tabulkách optimalizovaných pro paměť OLTP, viz Hash Indexy.
Nastavení velikosti pole indexu hash
Velikost pole hash je nastavena (bucket_count= value) kde value je celočíselná hodnota větší než nula. Pokud value není mocninou 2, skutečný počet bucket_count se zaokrouhlí nahoru na nejbližší vyšší mocninu 2. V naší ukázkové tabulce (bucket_count = 5 000 000), protože 5 000 000 není mocnina 2, skutečný počet segmentů se zaokrouhluje na 8 388 608 (2^23). Při výpočtu paměti potřebné pro pole hash je nutné použít toto číslo, nikoli 5 000 000.
V našem příkladu je tedy paměť potřebná pro každé pole hash:
8 388 608 * 8 = 2^23 * 8 = 2^23 * 2^3 = 2^26 = 67 108 864 nebo přibližně 64 MB.
Vzhledem k tomu, že máme tři indexy hash, paměť potřebná pro indexy hash je 3 × 64 MB = 192 MB.
Paměť pro neclusterované indexy
Neclusterované indexy se implementují jako Bw-stromy s vnitřními uzly obsahujícími hodnotu indexu a ukazatele na následující uzly. Uzly typu list obsahují hodnotu indexu a ukazatel na řádek tabulky v paměti.
Na rozdíl od indexů hash nemají neclusterované indexy pevnou velikost kontejneru. Index roste a zmenšuje se dynamicky s daty.
Paměť potřebnou neclusterovanými indexy je možné vypočítat následujícím způsobem:
Paměť přidělená nevětvovým uzlům
Pro typickou konfiguraci je paměť přidělená nelechotovým uzlům malá část celkové paměti, kterou index přijímá. To je tak malé, že se dá bezpečně ignorovat.Paměť pro listové uzly
Uzly typu list mají jeden řádek pro každý jedinečný klíč v tabulce, který odkazuje na datové řádky s tímto jedinečným klíčem. Pokud máte více řádků se stejným klíčem (to znamená, že máte neunikátní neclusterovaný index), je v uzlu typu list indexu pouze jeden řádek, který odkazuje na jeden z řádků, přičemž ostatní řádky jsou propojeny mezi sebou. Celková požadovaná paměť proto může být přibližná takto:- paměťProNeKlastrovanýIndex = (velikostUkazatele + součet(velikostiDatovýchTypůKlíčovýchSloupců)) * řádkySUnikátnímiKlíči
Neclusterované indexy jsou nejlepší při použití pro vyhledávání rozsahu, jak je znázorněno následujícím dotazem:
SELECT * FROM t_hk
WHERE c2 > 5;
Paměť pro verzování řádků
Aby nedocházelo k zámkům, In-Memory OLTP používá při aktualizaci nebo odstraňování řádků optimistickou souběžnost. To znamená, že při aktualizaci řádku se vytvoří jiná verze řádku. Odstranění jsou navíc logické – existující řádek se označí jako odstraněný, ale neodebere se okamžitě. Systém udržuje staré verze řádků (včetně odstraněných řádků) dostupné, dokud všechny transakce, které by mohly tyto verze použít, nedokončí své provádění.
Vzhledem k tomu, že v paměti může být kdykoli mnohem více řádků čekajících na cyklus uvolňování paměti, musíte mít dostatečnou paměť, aby pojmula tyto další řádky.
Počet řádků navíc se dá odhadnout tak, že vynásobíte maximální počet aktualizací a odstranění řádků za sekundu a pak ho vynásobíte počtem sekund, které nejdelší transakce trvá (minimálně 1).
Tato hodnota se pak vynásobí velikostí řádku, abyste získali počet bajtů, které potřebujete pro správu verzí řádků.
rowVersions = durationOfLongestTransactionInSeconds * peakNumberOfRowUpdatesOrDeletesPerSecond
Požadavky na paměť pro zastaralé řádky se pak odhadují vynásobením počtu zastaralých řádků velikostí řádku tabulky optimalizované pro paměť. Další informace naleznete v tématu Paměť pro tabulku.
memoryForRowVersions = rowVersions * rowSize
Paměť pro proměnné tabulky
Paměť používaná pro proměnnou tabulky se uvolní pouze v případě, že proměnná tabulky přestane být oborem. Odstraněné řádky, včetně těch odstraněných v rámci aktualizace, z proměnné tabulky nepodléhají procesu uvolňování paměti. Dokud proměnná tabulky neopustí rozsah, neuvolní se žádná paměť.
Proměnné tabulky definované ve velké dávce SQL místo uložené procedury a používané v mnoha transakcích můžou spotřebovávat velké množství paměti. Vzhledem k tomu, že nejsou automaticky uvolňovány z paměti, mohou odstraněné řádky v proměnné tabulky spotřebovat mnoho paměti a snižovat výkon, protože operace čtení musí procházet přes tyto odstraněné řádky.
Paměť pro růst
Předchozí výpočty odhadují potřeby paměti pro tabulku, jak aktuálně vypadá. Kromě této paměti je potřeba odhadnout růst tabulky a poskytnout dostatek paměti pro přizpůsobení tohoto růstu. Pokud například očekáváte 10% růstu, budete muset znásobit předchozí výsledky o 1,1, abyste získali celkovou paměť potřebnou pro vaši tabulku.
Fragmentace paměti
Aby se zabránilo režii při voláních pro přidělení paměti a zlepšil se výkon, In-Memory OLTP vždy požaduje paměť z operačního systému SQL Server (SQLOS) pomocí bloků o velikosti 64 kB, které se nazývají superbloky.
Každý superblok obsahuje přidělení paměti pouze v rámci konkrétního rozsahu velikostí, který se označuje jako sizeclass. Například superblok A může mít přidělení paměti ve třídě velikostí 1–16 bajtů, zatímco superblock B může mít přidělení paměti ve třídě velikostí 17–32 bajtů atd.
Ve výchozím nastavení jsou superbloky také dělené logickým procesorem. To znamená, že pro každý logický procesor existuje samostatná sada superbloků, dále rozdělená podle třídy velikostí. Tím se snižuje kolize přidělení paměti mezi požadavky spouštěnými na různých procesorech.
Když modul In-Memory OLTP vytvoří nové přidělení paměti, nejprve se pokusí najít volné paměti v existujícím superbloku pro požadovanou třídu velikostí a pro procesor zpracovávající požadavek. Pokud je tento pokus úspěšný, hodnota ve used_bytes sloupci ve sys.dm_xtp_system_memory_consumers pro konkrétního příjemce paměti se zvýší o požadovanou velikost paměti, ale hodnota ve allocated_bytes sloupci zůstane stejná.
Pokud v existujících superblocích není žádná volná paměť, přidělí se nový superblok a hodnota v used_bytes se zvýší o požadovanou velikost paměti, zatímco hodnota ve sloupci allocated_bytes se zvýší o 64 kB.
Vzhledem k tomu, že je paměť v superblokech přidělena a uvolněna, může být celkové množství paměti spotřebované modulem In-Memory OLTP výrazně větší než množství využité paměti. Jinými slovy, paměť může být fragmentovaná.
Uvolňování paměti může snížit využitou paměť, ale snižuje přidělenou paměť pouze v případě, že se jeden nebo více superbloků stane prázdným a uvolní se. To platí jak pro automatické, tak vynucené uvolňování paměti (garbage collection) pomocí systémové uložené procedury sys.sp_xtp_force_gc.
Pokud fragmentace paměti a přidělené využití paměti v rámci In-Memory OLTP modulu překročí očekávání, můžete povolit příznak trasování 9898. Tím se změní schéma dělení superbloku z jednotlivých procesorů na uzel NUMA, což snižuje celkový počet superbloků a potenciál pro vysokou fragmentaci paměti.
Tato optimalizace je relevantnější pro velké počítače s mnoha logickými procesory. Kompromisem této optimalizace je potenciální nárůst kolize přidělení paměti vyplývající z menšího počtu superbloků, což může snížit celkovou propustnost úloh. V závislosti na přístupových vzorcích zátěže může snížení propustnosti při použití dělení paměti na základě NUMA být patrné, nebo nemusí.
Související obsah
- Migrace na In-Memory OLTP