Sdílet prostřednictvím


Ukázková databáze pro In-Memory OLTP

platí pro:SQL Serverazure SQL Database

Přehled

Tato ukázka předvádí funkci In-Memory OLTP. Zobrazuje tabulky optimalizované pro paměť a nativně zkompilované uložené procedury a dá se použít k předvedení výhod výkonu In-Memory OLTP.

Poznámka

Chcete-li zobrazit toto téma pro SQL Server 2014 (12.x), viz Rozšíření AdventureWorks k předvedení In-Memory OLTP.

Ukázka migruje pět tabulek v databázi AdventureWorks2022 na optimalizaci pro paměť a zahrnuje ukázkové zatížení pro zpracování prodejních objednávek. Tuto ukázkovou úlohu můžete použít k zobrazení výhod výkonu In-Memory OLTP na vašem serveru.

V popisu ukázky probereme kompromisy, které byly provedeny při migraci tabulek do In-Memory OLTP, aby se zohlednily funkce, které nejsou (dosud) podporovány pro tabulky optimalizované pro paměť.

Dokumentace této ukázky je strukturovaná takto:

Požadavky

  • SQL Server 2016 (13.x)

  • Pro testování výkonu server se specifikacemi podobnými jako v produkčním prostředí. Pro tuto konkrétní ukázku byste měli mít k dispozici alespoň 16 GB paměti pro SQL Server. Obecné pokyny k hardwaru pro In-Memory OLTP najdete v následujícím blogovém příspěvku: Aspekty hardwaru pro In-Memory OLTP v SQL Serveru 2014

Instalace ukázky In-Memory OLTP na základě AdventureWorks

Při instalaci ukázky postupujte takto:

  1. Stáhněte si AdventureWorks2016_EXT.bak a SQLServer2016Samples.zip z: https://github.com/microsoft/sql-server-samples/releases/tag/adventureworks do místní složky, například C:\Temp.

  2. Obnovení zálohy databáze pomocí Transact-SQL nebo aplikace SQL Server Management Studio:

    1. Identifikace cílové složky a názvu souboru pro datový soubor, například

      'h:\DATA\AdventureWorks2022_Data.mdf'

    2. Identifikace cílové složky a názvu souboru protokolu, například

      'i:\DATA\AdventureWorks2022_log.ldf'

      1. Soubor protokolu by se měl umístit na jinou jednotku než datový soubor, ideálně na jednotku s nízkou latencí, jako je úložiště SSD nebo PCIe, pro dosažení maximálního výkonu.

    Ukázkový skript T-SQL:

    RESTORE DATABASE [AdventureWorks2022]   
      FROM DISK = N'C:\temp\AdventureWorks2022.bak'   
        WITH FILE = 1,    
      MOVE N'AdventureWorks2022_Data' TO N'h:\DATA\AdventureWorks2022_Data.mdf',    
      MOVE N'AdventureWorks2022_Log' TO N'i:\DATA\AdventureWorks2022_log.ldf',  
      MOVE N'AdventureWorks2022_mod' TO N'h:\data\AdventureWorks2022_mod'  
     GO  
    
  3. Pokud chcete zobrazit ukázkové skripty a úlohy, rozbalte soubor SQLServer2016Samples.zip do místní složky. Pokyny ke spuštění úlohy najdete v souboru In-Memory\readme.txt OLTP.

Popis ukázkových tabulek a postupů

Vzor vytvoří nové tabulky pro produkty a prodejní objednávky na základě existujících tabulek v AdventureWorks2022. Schéma nových tabulek je podobné existujícím tabulkám s několika rozdíly, jak je vysvětleno níže.

Nové tabulky optimalizované pro paměť mají příponu "_inmem". Ukázka také obsahuje odpovídající tabulky s příponou "_ondisk" – tyto tabulky je možné použít k porovnání mezi výkonem tabulek optimalizovaných pro paměť a tabulek založených na disku ve vašem systému.

Tabulky optimalizované pro paměť používané v úloze pro porovnání výkonu jsou plně odolné a plně protokolované. Nenabízejí stálost ani spolehlivost, aby dosáhli zvýšení výkonu.

Cílovou úlohou pro tuto ukázku je zpracování prodejních objednávek, kde zvažujeme také informace o produktech a slevách. K tomuto účelu používáme tabulky SalesOrderHeader, SalesOrderDetail, Product, SpecialOffera SpecialOfferProduct.

Dvě nové uložené procedury, Sales.usp_InsertSalesOrder_inmem a Sales.usp_UpdateSalesOrderShipInfo_inmem, se používají k vložení prodejních objednávek a k aktualizaci informací o expedici dané prodejní objednávky.

Nová Demo schématu obsahuje pomocné tabulky a uložené procedury pro provedení ukázkové úlohy.

Konkrétně In-Memory ukázka OLTP přidá do AdventureWorks2022následující objekty:

Tabulky přidané vzorovým souborem

Nové tabulky

Sales.SalesOrderHeader_inmem

  • Záhlaví informací o prodejních objednávkách Každá prodejní objednávka má v této tabulce jeden řádek.

Sales.SalesOrderDetail_inmem

  • Podrobnosti o prodejních objednávkách Každá položka řádku prodejní objednávky má v této tabulce jeden řádek.

Sales.SpecialOffer_inmem

  • Informace o speciálních nabídkách, včetně procenta slevy spojeného s každou speciální nabídkou.

Sales.SpecialOfferProduct_inmem

  • Referenční tabulka pro srovnání akčních nabídek a produktů Každá speciální nabídka může obsahovat nula nebo více produktů a každý produkt může být zahrnut v nule nebo více speciálních nabídkách.

Production.Product_inmem

  • Informace o produktech, včetně jejich ceníkové ceny.

Demo.DemoSalesOrderDetailSeed

  • Používá se v ukázkové úloze k vytvoření ukázkových prodejních objednávek.

Varianty tabulek založené na disku:

  • Sales.SalesOrderHeader_ondisk

  • Sales.SalesOrderDetail_ondisk

  • Sales.SpecialOffer_ondisk

  • Sales.SpecialOfferProduct_ondisk

  • Production.Product_ondisk

Rozdíly mezi původními tabulkami založenými na disku a novými tabulkami optimalizovanými pro paměť

Ve většině případů nové tabulky zavedené touto ukázkou používají stejné sloupce a stejné datové typy jako původní tabulky. Existuje však několik rozdílů. Níže uvádíme rozdíly spolu s odůvodněním změn.

Sales.SalesOrderHeader_inmem

  • Výchozí omezení jsou podporována pro tabulky optimalizované pro paměť a většinu výchozích omezení jsme migrovali bez změny. Původní tabulka Sales.SalesOrderHeader však obsahuje dvě výchozí omezení, která načítají aktuální datum pro sloupce OrderDate a ModifiedDate. V úloze zpracování s vysokou propustností a velkou souběžností se může jakýkoli globální prostředek stát bodem sporu. Systémový čas je takový globální prostředek a zjistili jsme, že se může stát kritickým bodem při spuštění úlohy OLTP In-Memory, která vkládá prodejní objednávky, zejména pokud je potřeba načíst systémový čas pro více sloupců v záhlaví prodejní objednávky a také podrobnosti prodejní objednávky. Problém je vyřešený v této ukázce načtením systémového času pouze jednou pro každou vloženou prodejní objednávku a použitím této hodnoty pro sloupce datetime v SalesOrderHeader_inmem a SalesOrderDetail_inmemv uložené proceduře Sales.usp_InsertSalesOrder_inmem.

  • Alias uživatelsky definované datové typy (UDT) – původní tabulka používá dva aliasy UDT dbo.OrderNumber a dbo.AccountNumberpro sloupce PurchaseOrderNumber a AccountNumber. SQL Server 2016 (13.x) nepodporuje alias UDT pro tabulky optimalizované pro paměť, takže nové tabulky používají systémové datové typy nvarchar(25) a nvarchar(15).

  • Sloupce povolující hodnotu null v klíčích indexu – v původní tabulce je sloupec SalesPersonID povolující hodnotu null, zatímco v nových tabulkách není sloupec povolující hodnotu null a má výchozí omezení s hodnotou (-1). Důvodem je, že indexy v tabulkách optimalizovaných pro paměť nemohou mít v klíči indexu sloupce s možnou hodnotou null; -1 je v tomto případě náhradní hodnotou NULL.

  • Počítané sloupce – počítané sloupce SalesOrderNumber a TotalDue jsou vynechány, protože SQL Server 2016 (13.x) nepodporuje počítané sloupce v tabulkách optimalizovaných pro paměť. Nové zobrazení Sales.vSalesOrderHeader_extended_inmem odráží sloupce SalesOrderNumber a TotalDue. Toto zobrazení proto můžete použít, pokud jsou tyto sloupce potřeba.

    • platí pro: SQL Server 2017 (14.x) CTP 1.1.
      Počínaje SQL Serverem 2017 (14.x) CTP 1.1 se vypočítané sloupce podporují v paměťově optimalizovaných tabulkách a indexech.
  • omezení cizího klíče jsou podporována pro tabulky optimalizované pro paměť v SQL Serveru 2016 (13.x), ale pouze pokud jsou odkazované tabulky také optimalizovány pro paměť. Cizí klíče, které odkazují na tabulky, jež jsou také migrovány jako optimalizované pro paměť, se zachovávají v těchto migrovaných tabulkách, zatímco ostatní cizí klíče jsou vynechány. Kromě toho je SalesOrderHeader_inmem v ukázkové úloze horkou tabulkou a omezení cizích klíčů vyžadují další zpracování pro všechny operace DML, protože vyžaduje vyhledávání ve všech ostatních tabulkách odkazovaných v těchto omezeních. Předpokladem je proto, že aplikace zajišťuje referenční integritu pro tabulku Sales.SalesOrderHeader_inmem a při vložení řádků se neověřuje referenční integrita.

  • Sloupec rowguid je vynechán. I když se u tabulek optimalizovaných pro paměť podporuje uniqueidentifier, možnost ROWGUIDCOL se v SQL Serveru 2016 (13.x) nepodporuje. Sloupce tohoto typu se obvykle používají buď pro slučování replikací, nebo pro tabulky, které obsahují filestream sloupce. Tato ukázka neobsahuje ani jedno.

Prodej.PoložkaObjednávky

  • výchozí omezení – podobně jako SalesOrderHeader, výchozí omezení vyžadující systémové datum a čas se nemigruje, místo toho se uložená procedura vkládání prodejních objednávek postará o vložení aktuálního systémového data a času při prvním vložení.

  • vypočítané sloupce – vypočítaný sloupec LineTotal nebyl migrován, protože počítané sloupce nejsou podporované u tabulek optimalizovaných pro paměť v SQL Serveru 2016 (13.x). Pro přístup k tomuto sloupci použijte zobrazení Sales.vSalesOrderDetail_extended_inmem.

  • Rowguid – sloupec rowguid je vynechán. Podrobnosti najdete v popisu tabulky SalesOrderHeader.

Výroba.Produkt

  • UDT alias – původní tabulka používá uživatelsky definovaný datový typ dbo.Flag, který je ekvivalentní systémovému datovému typu bit. Migrovaná tabulka místo toho používá datový typ bitů.

  • Rowguid – sloupec rowguid je vynechán. Podrobnosti najdete v popisu tabulky SalesOrderHeader.

Prodej.SpeciálníNabídka

  • Rowguid – sloupec rowguid je vynechán. Podrobnosti najdete v popisu tabulky SalesOrderHeader.

Prodej.ZvláštníNabídkaProdukt

  • Rowguid – sloupec rowguid je vynechán. Podrobnosti najdete v popisu tabulky SalesOrderHeader.

Důležité informace o indexech v tabulkách optimalizovaných pro paměť

Směrný index pro tabulky optimalizované pro paměť je index NONCLUSTERED, který podporuje bodová vyhledávání (vyhledávání indexu na predikátu rovnosti), prohledávání rozsahů (prohledávání indexu v predikátu nerovnosti), úplné prohledávání indexu a seřazené prohledávání. Kromě toho indexy NONCLUSTERED podporují vyhledávání na úvodních sloupcích klíče indexu. Ve skutečnosti indexy optimalizované pro paměť podporují všechny operace podporované neclusterovanými indexy založené na disku, přičemž jedinou výjimkou je zpětná kontrola. Proto je použití NEKLASTROVANÝCH indexů bezpečnou volbou pro vaše indexy.

Indexy HASH je možné použít k další optimalizaci úlohy. Jsou optimalizované pro vyhledávání bodů a vkládání řádků. Je však nutné vzít v úvahu, že nepodporují prohledávání rozsahů, seřazené prohledávání nebo vyhledávání v úvodních sloupcích klíčů indexu. Proto je potřeba při použití těchto indexů věnovat pozornost. Kromě toho je nutné zadat bucket_count při vytváření. Obvykle by měla být nastavena mezi jednou a dvěma násobky počtu hodnot klíče indexu, ale nadhodnocení obvykle není problém.

Další informace:

Indexy migrovaných tabulek byly vyladěny pro ukázkové úlohy zpracování prodejních objednávek. Úloha spoléhá na vkládání a bodové vyhledávání v tabulkách Sales.SalesOrderHeader_inmem a Sales.SalesOrderDetail_inmema také spoléhá na vyhledávání bodů na sloupcích primárního klíče v tabulkách Production.Product_inmem a Sales.SpecialOffer_inmem.

Sales.SalesOrderHeader_inmem má tři indexy, které jsou všechny HASH indexy kvůli výkonu a protože pro úlohu nejsou potřeba žádné seřazené skeny ani skeny rozsahu.

  • Index HASH na (SalesOrderID): bucket_count má velikost 10 milionů (zaokrouhleno nahoru na 16 milionů), protože očekávaný počet prodejních objednávek je 10 milionů.

  • HASH index na (SalesPersonID): bucket_count je 1 milion. Zadaná datová sada nemá mnoho prodejních osob. Ale toto velké "bucket_count" umožňuje budoucí růst. Navíc neplatíte pokutu za bodové vyhledávání, pokud je bucket_count překilován.

  • HASH indexu na (CustomerID): bucket_count je 1 milion. Zadaná datová sada nemá mnoho zákazníků, ale to umožňuje budoucí růst.

Sales.SalesOrderDetail_inmem má tři indexy, které jsou všechny hashovací indexy kvůli výkonu a také proto, že pro úlohu nejsou potřeba žádné seřazené skeny ani skeny rozsahu.

  • HASH index na (SalesOrderID, SalesOrderDetailID): toto je index primárního klíče, a i když vyhledávání přes (SalesOrderID, SalesOrderDetailID) budou málo častá, použití hash indexu pro klíč urychlí vkládání řádků. Bucket_count má velikost 50 milionů (zaokrouhleno nahoru na 67 milionů): očekávaný počet prodejních objednávek je 10 milionů a velikost je v průměru pět položek na objednávku.

  • Index HASH pro (SalesOrderID): vyhledávání podle prodejní objednávky jsou časté: budete chtít najít všechny řádkové položky odpovídající jedné objednávce. Očekávaný počet prodejních objednávek je 10 milionů, proto má bucket_count velikost 10 milionů (zaokrouhleno nahoru na 16 milionů).

  • Index HASH na (ProductID): bucket_count je 1 milion. Zadaná datová sada nemá velké množství produktů, ale to umožňuje budoucí růst.

Production.Product_inmem má tři indexy

  • Index HASH on (ProductID): vyhledávání na ProductID jsou v kritické cestě pro ukázkovou úlohu, proto se jedná o index hash.

  • Neklastrovaný index na (Name): To umožní provádění seřazeného skenování názvů produktů

  • Neclusterovaný index na (ProductNumber): Tím se umožní seřazené prohledávání čísel produktů.

Sales.SpecialOffer_inmem má jeden index HASH (SpecialOfferID): vyhledávání bodů speciálních nabídek je v kritické části ukázkové úlohy. bucket_count má velikost 1 milion, aby umožnil budoucí růst.

Sales.SpecialOfferProduct_inmem se v ukázkové úloze neodkazuje, a proto není nutné k optimalizaci úlohy použít indexy hash – indexy pro (SpecialOfferID, ProductID) a (ProductID) jsou neclusterované.

Všimněte si, že ve výše uvedených počtech segmentů jsou některé nadlimitní, ale nikoli počty segmentů pro indexy na SalesOrderHeader_inmem a SalesOrderDetail_inmem, které mají velikost pouze pro 10 milionů prodejních objednávek. To bylo provedeno tak, aby bylo možné nainstalovat ukázku do systémů s nízkou dostupností paměti, i když v takových případech ukázková úloha selže s nedostatkem paměti. Pokud chcete škálovat mnohem více než 10 milionů prodejních objednávek, můžete počet kontejnerů odpovídajícím způsobem zvýšit.

Důležité informace o využití paměti

Využití paměti v ukázkové databázi, a to jak před a po spuštění ukázkové úlohy, je popsáno v části Využití paměti pro tabulky optimalizované pro paměť.

Uložené procedury přidané vzorkovým příkladem

Dva klíčové uložené procedury pro vložení prodejní objednávky a aktualizaci podrobností o expedici jsou následující:

  • Sales.usp_InsertSalesOrder_inmem

    • Vloží do databáze novou prodejní objednávku a vypíše SalesOrderID pro danou prodejní objednávku. Jako vstupní parametry přebírá podrobnosti pro záhlaví prodejní objednávky a také řádkové položky v objednávce.

    • Výstupní parametr:

      • @SalesOrderID int – SalesOrderID pro prodejní objednávku, která byla právě vložena
    • Vstupní parametry (povinné):

      • @DueDate datetime2

      • @CustomerID int

      • @BillToAddressID [int]

      • @ShipToAddressID [int]

      • @ShipMethodID [int]

      • @SalesOrderDetails Sales.SalesOrderDetailType_inmem – parametr s tabulkovými hodnotami (TVP), který obsahuje položky řádků objednávky

    • Vstupní parametry (volitelné):

      • @Status [tinyint]

      • @OnlineOrderFlag [bit]

      • @PurchaseOrderNumber [nvarchar](25)

      • @AccountNumber [nvarchar](15)

      • @SalesPersonID [int]

      • @TerritoryID [int]

      • @CreditCardID [int]

      • @CreditCardApprovalCode [varchar](15)

      • @CurrencyRateID [int]

      • @Comment nvarchar(128)

  • Sales.usp_UpdateSalesOrderShipInfo_inmem

    • Aktualizujte informace o expedici pro danou prodejní objednávku. Tím se také aktualizují informace o expedici pro všechny řádkové položky prodejní objednávky.

    • Jedná se o zástupnou proceduru pro nativně zkompilované uložené procedury Sales.usp_UpdateSalesOrderShipInfo_native s logikou opakování, která řeší (neočekávané) konflikty se souběžnými transakcemi, které aktualizují stejnou objednávku. Další informace najdete v tématu logiky opakování.

  • Sales.usp_UpdateSalesOrderShipInfo_native

    • Jedná se o nativně zkompilovanou uloženou proceduru, která skutečně zpracovává aktualizaci expedičních informací. To znamená, že je volána z obálky uložená procedura Sales.usp_UpdateSalesOrderShipInfo_inmem. Pokud se klient dokáže vypořádat se selháními a implementuje logiku opakování, můžete tento postup volat přímo, místo abyste použili obalovou uloženou proceduru.

Pro ukázkové úlohy se používá následující uložená procedura.

  • Demo.usp_DemoReset

    • Obnoví ukázku vyprázdněním a znovu osazením tabulek SalesOrderHeader a SalesOrderDetail.

Následující uložené procedury se používají k vkládání a odstraňování z tabulek optimalizovaných pro paměť a současně zaručují integritu domény a referenční integrity.

  • Production.usp_InsertProduct_inmem

  • Production.usp_DeleteProduct_inmem

  • Sales.usp_InsertSpecialOffer_inmem

  • Sales.usp_DeleteSpecialOffer_inmem

  • Sales.usp_InsertSpecialOfferProduct_inmem

Nakonec se k ověření domény a referenční integrity používá následující uložená procedura.

  1. dbo.usp_ValidateIntegrity

    • Volitelný parametr: @object_id – ID objektu k ověření integrity pro

    • Tento postup spoléhá na tabulky dbo.DomainIntegrity, dbo.ReferentialIntegritya dbo.UniqueIntegrity pro pravidla integrity, která je potřeba ověřit – ukázka naplní tyto tabulky na základě kontrol, cizího klíče a jedinečných omezení, která existují pro původní tabulky v databázi AdventureWorks2022.

    • Spoléhá na pomocné postupy dbo.usp_GenerateCKCheck, dbo.usp_GenerateFKChecka dbo.GenerateUQCheck k vygenerování T-SQL potřebného k provádění kontrol integrity.

Měření výkonu pomocí ukázkové úlohy

Ostress je nástroj příkazového řádku vyvinutý týmem podpory Microsoft CSS SQL Serveru. Tento nástroj lze použít ke spouštění dotazů nebo paralelnímu spouštění uložených procedur. Můžete nakonfigurovat počet vláken pro paralelní spuštění daného příkazu T-SQL a můžete určit, kolikrát má být příkaz proveden v tomto vlákně; ostress spustí vlákna a spustí příkaz na všech vláknech paralelně. Po dokončení provádění všech vláken ostress nahlásí čas potřebný k dokončení provádění všech vláken.

Instalace ostress

Ostress se instaluje jako součást nástrojů RML (Report Markup Language). pro ostress neexistuje samostatná instalace.

Postup instalace:

  1. Stáhněte a spusťte instalační balíček x64 pro nástroje RML z následující stránky: Stáhnout RML pro SQL Server

  2. Pokud existuje dialogové okno s informací, že se určité soubory používají, vyberte Pokračovat.

Spouštění ostress

Ostress se spouští z příkazového řádku. Nejpohodlnější je spustit nástroj z příkazového řádku RML, který je nainstalován jako součást nástrojů RML.

Pokud chcete otevřít příkazový řádek RML, postupujte podle těchto pokynů:

Ve Windows otevřete nabídku Start tak, že vyberete klávesu Windows a zadáte rml. Vyberte příkaz "RML Cmd Prompt", který bude v seznamu výsledků hledání.

Ujistěte se, že je příkazový řádek umístěný v instalační složce nástrojů RML.

Možnosti příkazového řádku pro ostress se dají zobrazit, když jednoduše spustíte ostress.exe bez jakýchkoli možností příkazového řádku. Mezi hlavní možnosti, které je potřeba zvážit při spuštění ostress s touto ukázkou, patří:

  • -S název instance Microsoft SQL Server, ke které se chcete připojit

  • -E používá ověřování systému Windows pro připojení (výchozí); pokud používáte ověřování SQL Serveru, použijte možnosti -U a -P k zadání uživatelského jména a hesla.

  • -d název databáze, v tomto příkladu AdventureWorks2022

  • -Q příkazu T-SQL, který se má spustit.

  • -n počet připojení zpracovávající každý vstupní soubor nebo dotaz

  • -r počet iterací pro každé připojení k provedení každého vstupního souboru/dotazu

Ukázková úloha

Hlavní uložená procedura použitá v ukázkové úloze je Sales.usp_InsertSalesOrder_inmem/ondisk. Skript v následujícím příkladu vytvoří parametr typu tabulky (TVP) s ukázkovými daty a zavolá proceduru pro vložení prodejní objednávky s pěti řádkovými položkami.

Nástroj ostress slouží k paralelnímu spouštění volání uložených procedur za účelem simulace souběžného vkládání prodejních objednávek klienty.

Po každém spuštění zátěže resetujte demonstraci Demo.usp_DemoReset. Tento postup odstraní řádky v tabulkách optimalizovaných pro paměť, zkrátí diskové tabulky a spustí kontrolní bod databáze.

Následující skript se spustí souběžně, aby simuloval úlohu zpracování prodejních objednávek:

DECLARE   
      @i int = 0,   
      @od Sales.SalesOrderDetailType_inmem,   
      @SalesOrderID int,   
      @DueDate datetime2 = sysdatetime(),   
      @CustomerID int = rand() * 8000,   
      @BillToAddressID int = rand() * 10000,   
      @ShipToAddressID int = rand() * 10000,   
      @ShipMethodID int = (rand() * 5) + 1;   
  
INSERT INTO @od   
SELECT OrderQty, ProductID, SpecialOfferID   
FROM Demo.DemoSalesOrderDetailSeed   
WHERE OrderID= cast((rand()*106) + 1 as int);   
  
WHILE (@i < 20)   
BEGIN;   
      EXEC Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od;   
      SET @i += 1   
END

Ve skriptu je každá vytvořená vzorová objednávka 20krát vložena pomocí 20 uložených procedur, které jsou spuštěny ve smyčce WHILE. Smyčka se používá k zohlednění skutečnosti, že se databáze používá k vytvoření pořadí vzorků. V typických produkčních prostředích aplikace střední vrstvy vytvoří prodejní objednávku, která se má vložit.

Výše uvedený skript vloží prodejní objednávky do tabulek optimalizovaných pro paměť. Skript pro vložení prodejních objednávek do tabulek založených na disku je odvozen nahrazením dvou výskytů "_inmem" za "_ondisk".

Pomocí nástroje ostress spustíme skripty pomocí několika souběžných připojení. K řízení počtu připojení použijeme parametr -n a parametr r k řízení počtu spuštění skriptu v každém připojení.

Spuštění úlohy

Abychom mohli testovat ve velkém měřítku, vložíme 10 milionů prodejních objednávek pomocí 100 připojení. Tento test běží přiměřeně dobře na skromném serveru (například s 8 fyzickými a 16 logickými jádry) a základním úložišti SSD pro protokol. Pokud test na vašem hardwaru neběží dobře, podívejte se na část Řešení potíží s pomalými testy. Pokud chcete snížit úroveň stresu pro tento test, snižte počet připojení změnou parametru -n. Pokud chcete například snížit počet připojení na 40, změňte parametr -n100 na -n40.

Jako měřítko výkonu pro úlohu používáme uplynulý čas hlášený ostress.exe po spuštění úlohy.

Následující pokyny a měření používají úlohu, která vloží 10 milionů prodejních objednávek. Pokyny ke spuštění menší úlohy, která vkládá 1 milion prodejních objednávek, najdete v archivuIn-Memory OLTP\readme.txt' that is part of the SQLServer2016Samples.zip.

Tabulky optimalizované pro paměť

Začneme spuštěním úlohy v tabulkách optimalizovaných pro paměť. Následující příkaz otevře 100 vláken, z nichž každá běží pro 5 000 iterací. Každá iterace vloží 20 prodejních objednávek do samostatných transakcí. K dispozici je 20 vložení na iteraci, které kompenzují skutečnost, že se databáze používá ke generování dat, která se mají vložit. Výsledkem je celkem 20 × 5 000 × 100 = 10 000 000 vložení prodejní objednávky.

Otevřete příkazový řádek RML a spusťte následující příkaz:

Výběrem tlačítka Kopírovat příkaz zkopírujte a vložte ho do příkazového řádku nástrojů RML.

ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2022 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_inmem, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID int = (rand() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID= cast((rand()*106) + 1 as int); while (@i < 20) begin; EXEC Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"  

Na jednom testovacím serveru s celkovým počtem 8 fyzických (16 logických) jader to trvalo 2 minuty a 5 sekund. Na druhém testovacím serveru s 24 fyzickými (48 logickými) jádry to trvalo 1 minutu a 0 sekund.

Sledujte využití procesoru, když je úloha spuštěná, například pomocí správce úloh. Uvidíte, že využití procesoru je blízko 100%. Pokud tomu tak není, máte kritický bod vstupně-výstupních operací protokolu, viz také Řešení potíží s pomalými testy.

Diskové tabulky

Následující příkaz spustí úlohu v tabulkách založených na disku. Spuštění této úlohy může trvat déle, což je z velké části způsobeno soupeřením o zámky v systému. Tabulky optimalizované pro paměť jsou bez zámků, a proto netrpí tímto problémem.

Otevřete příkazový řádek RML a spusťte následující příkaz:

Výběrem tlačítka Kopírovat zkopírujte příkaz a vložte ho do příkazového řádku nástrojů RML.

ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2022 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_ondisk, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID int = (rand() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID= cast((rand()*106) + 1 as int); while (@i < 20) begin; EXEC Sales.usp_InsertSalesOrder_ondisk @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"  

Na jednom testovacím serveru s celkovým počtem 8 fyzických (16 logických) jader to trvalo 41 minut a 25 sekund. Na druhém testovacím serveru s 24 fyzickými (48 logickými) jádry to trvalo 52 minut a 16 sekund.

Hlavním faktorem rozdílu výkonu mezi tabulkami optimalizovanými pro paměť a diskovými tabulkami v tomto testu je skutečnost, že při použití tabulek založených na disku sql Server nemůže plně využívat procesor. Důvodem je konflikt zámků: souběžné transakce se pokouší zapsat na stejnou datovou stránku; západky se používají k zajištění, že pouze jedna transakce může zapisovat na stránku najednou. Modul In-Memory OLTP je bez západky a datové řádky nejsou uspořádané na stránkách. Souběžné transakce tedy navzájem neblokují vložení, takže SQL Server plně využívá procesor.

Využití procesoru můžete sledovat, když je úloha spuštěná, například pomocí správce úloh. U tabulek založených na disku uvidíte, že využití procesoru je daleko od 100%. Při testovací konfiguraci s 16 logickými procesory by se využití pohybovalo kolem 24%.

Volitelně můžete pomocí nástroje Performance Monitor zobrazit číslo západek za sekundu s čítačem výkonu \SQL Server:Latches\Latch Waits/sec.

Resetování ukázky

Pokud chcete ukázku resetovat, otevřete příkazový řádek RML a spusťte následující příkaz:

ostress.exe -S. -E -dAdventureWorks2022 -Q"EXEC Demo.usp_DemoReset"  

V závislosti na hardwaru to může trvat několik minut.

Po každém spuštění ukázky doporučujeme resetovat. Protože tato úloha je pouze pro vkládání, každé spuštění opotřebovává více paměti, a proto je nutné resetování, aby se předešlo vyčerpání paměti. Množství paměti spotřebované po spuštění je popsáno v oddílu Využití paměti po spuštění úlohy.

Řešení potíží s pomalými testy

Výsledky testů se obvykle liší podle hardwaru a také úrovně souběžnosti používané při testovacím běhu. Pokud výsledky nejsou takové, jaké byste očekávali, zaměřte se na několik věcí:

  • Počet souběžných transakcí: Při spouštění úlohy v jednom vlákně bude zvýšení výkonu s In-Memory OLTP pravděpodobně menší než 2X. Zámek je problémem pouze při vysoké úrovni souběžnosti.

  • Nízký počet jader dostupných pro SQL Server: To znamená, že v systému bude nízká úroveň souběžnosti, protože pro SQL může být k dispozici pouze tolik souběžných transakcí, kolik je dostupných jader.

    • Příznak: Pokud je využití procesoru vysoké při spouštění zatížení na diskových tabulkách, znamená to, že neexistuje mnoho konfliktů, což poukazuje na nedostatek souběžnosti.
  • Rychlost jednotky protokolu: Pokud jednotka protokolu nemůže držet krok s úrovní propustnosti transakcí v systému, úloha se stane kritickým bodem při vstupně-výstupních operacích protokolu. Ačkoliv je logování efektivnější s In-Memory OLTP, pokud je logovací IO kritickým bodem, potenciální zvýšení výkonu je omezený.

    • Příznak: Pokud využití procesoru není blízko 100% nebo je při spuštění úloh na tabulkách optimalizovaných pro paměť výrazně kolísavé, je možné, že dojde k úzkému hrdlu operací vstupu a výstupu protokolu. To můžete potvrdit otevřením aplikace Resource Monitor a zobrazením délky fronty protokolové jednotky.

Využití paměti a místa na disku v ukázce

V následujícím příkladu popisujeme, co očekávat z hlediska využití paměti a místa na disku pro ukázkovou databázi. Ukážeme si také výsledky, které jsme viděli na testovacím serveru s 16 logickými jádry.

Využití paměti pro tabulky optimalizované pro paměť

Celkové využití databáze

Následující dotaz lze použít k získání celkového využití paměti pro In-Memory OLTP v systému.

SELECT type  
   , name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'  

Snímek po vytvoření databáze:

typ Jméno pages_MB
MEMORYCLERK_XTP Výchozí 94
MEMORYCLERK_XTP DB_ID_5 877
MEMORYCLERK_XTP Výchozí 0
MEMORYCLERK_XTP Výchozí 0

Standardní správci paměti obsahují systémové struktury paměti a jsou relativně malé. Správce paměti pro uživatelskou databázi, což je v tomto případě databáze s ID číslo 5 (database_id se může ve vaší instanci lišit), má přibližně 900 MB.

Využití paměti na tabulku

Pomocí následujícího dotazu můžete přejít k podrobnostem o využití paměti jednotlivých tabulek a jejich indexů:

SELECT object_name(t.object_id) AS [Table Name]  
     , memory_allocated_for_table_kb  
 , memory_allocated_for_indexes_kb  
FROM sys.dm_db_xtp_table_memory_stats dms JOIN sys.tables t   
ON dms.object_id=t.object_id  
WHERE t.type='U';  

Následující tabulka zobrazuje výsledky tohoto dotazu pro čerstvou instalaci ukázky:

Název tabulky pamet_alokovana_pro_tabulku_kb paměť_přidělena_pro_indexy_kb
SpeciálníNabídkaProdukt_inmem 64 3840
DemoSalesOrderHeaderSeed 1984 5504
SalesOrderDetail_inmem 15316 663552
DemoDetailProdejníObjednávkySemínko 64 10432
SpecialOffer_inmem 3 8192
SalesOrderHeader_inmem 7168 147456
Product_inmem 124 12352

Jak vidíte, tabulky jsou poměrně malé: SalesOrderHeader_inmem je asi 7 MB a SalesOrderDetail_inmem je o velikosti přibližně 15 MB.

To, co je zde zajímavé, je velikost paměti přidělené indexům v porovnání s velikostí dat tabulky. Je to proto, že indexy hash v ukázce jsou předem velké pro větší velikost dat. Všimněte si, že indexy hash mají pevnou velikost, a proto se jejich velikost nezvětšuje s velikostí dat v tabulce.

Využití paměti po spuštění úlohy

Po vložení 10 milionů prodejních objednávek vypadá veškeré využití paměti podobně jako následující:

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'  
typ Jméno stránky_MB
MEMORYCLERK_XTP Výchozí 146
MEMORYCLERK_XTP DB_ID_5 7374
MEMORYCLERK_XTP Výchozí 0
MEMORYCLERK_XTP Výchozí 0

Jak vidíte, SQL Server používá pro tabulky optimalizované pro paměť a indexy v ukázkové databázi bitovou velikost pod 8 GB.

Zobrazení podrobného využití paměti pro jednotlivé tabulky po jednom vzorovém spuštění:

SELECT object_name(t.object_id) AS [Table Name]  
     , memory_allocated_for_table_kb  
 , memory_allocated_for_indexes_kb  
FROM sys.dm_db_xtp_table_memory_stats dms JOIN sys.tables t   
ON dms.object_id=t.object_id  
WHERE t.type='U'  
Název tabulky paměť_přidělena_pro_tabulku_kb paměť alokovaná pro indexy v kB
SalesOrderDetail_inmem 5113761 663552
DemoSalesOrderDetailSeed 64 10368
Speciální nabídka_inmem 2 8192
SalesOrderHeader_inmem 1575679 147456
Product_inmem 111 12032
SpecialOfferProduct_inmem 64 3712
DemoSalesOrderHeaderSeed 1984 5504

Vidíme celkem přibližně 6,5 GB dat. Všimněte si, že velikost indexů v tabulce SalesOrderHeader_inmem a SalesOrderDetail_inmem je stejná jako velikost indexů před vložením prodejních objednávek. Velikost indexu se nezměnila, protože obě tabulky používají indexy hash a indexy hash jsou statické.

Po resetování demo

Uložená procedura Demo.usp_DemoReset může být použita k resetování ukázky. Odstraní data v tabulkách SalesOrderHeader_inmem a SalesOrderDetail_inmema znovu zasadí data z původních tabulek SalesOrderHeader a SalesOrderDetail.

I když byly řádky v tabulkách odstraněny, neznamená to, že se paměť okamžitě uvolní. SQL Server podle potřeby uvolní paměť z odstraněných řádků v tabulkách optimalizovaných pro paměť na pozadí. Uvidíte, že okamžitě po resetu dema, při absenci transakčního zatížení na systému, paměť z odstraněných řádků ještě není uvolněna:

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%';
typ Jméno stránky_MB
MEMORYCLERK_XTP Výchozí 2261
MEMORYCLERK_XTP DB_ID_5 7396
MEMORYCLERK_XTP Výchozí 0
MEMORYCLERK_XTP Výchozí 0

Očekává se to: paměť bude uvolněna při spuštění transakční úlohy.

Pokud spustíte druhé spuštění ukázkové úlohy, zpočátku se sníží využití paměti, protože dříve odstraněné řádky se již vyčistily. V určitém okamžiku se velikost paměti znovu zvětší, dokud se úloha nedokončí. Po vložení 10 milionů řádků po ukázkovém resetování bude využití paměti velmi podobné využití po prvním spuštění. Například:

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%';
typ Jméno stránky_MB
MEMORYCLERK_XTP Výchozí 1863
MEMORYCLERK_XTP DB_ID_5 7390
MEMORYCLERK_XTP Výchozí 0
MEMORYCLERK_XTP Výchozí 0

Využití disku pro tabulky optimalizované pro paměť

Celkovou velikost disku pro soubory kontrolních bodů databáze v daném okamžiku najdete pomocí dotazu:

SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]  
FROM sys.filegroups f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX';  
  

Počáteční stav

Při počátečním vytvoření ukázkové skupiny souborů a ukázkových tabulek optimalizovaných pro paměť se vytvoří několik souborů kontrolních bodů a systém začne zaplnit soubory – počet předem vytvořených souborů kontrolních bodů závisí na počtu logických procesorů v systému. Vzhledem k tomu, že je ukázka zpočátku velmi malá, předem vytvořené soubory budou po počátečním vytvoření většinou prázdné.

Následující kód ukazuje počáteční velikost na disku pro ukázku na počítači s 16 logickými procesory:

SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]  
FROM sys.filegroups f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX';  
Velikost na disku v MB
2312

Jak vidíte, mezi velikostí kontrolních bodů uložených na disku, která činí 2,3 GB, a skutečnou velikostí dat, která je blíže 30 MB, je značný rozdíl.

Když se podíváte blíže na to, odkud pochází využití místa na disku, můžete použít následující dotaz. Velikost disku vráceného tímto dotazem je přibližná pro soubory se stavem 5 (VYŽADOVÁNO PRO ZÁLOHOVÁNÍ/HA), 6 (V PŘEVODU NA TOMBSTONE) nebo 7 (TOMBSTONE).

SELECT state_desc  
 , file_type_desc  
 , COUNT(*) AS [count]  
 , SUM(CASE  
   WHEN state = 5 AND file_type=0 THEN 128*1024*1024  
   WHEN state = 5 AND file_type=1 THEN 8*1024*1024  
   WHEN state IN (6,7) THEN 68*1024*1024  
   ELSE file_size_in_bytes  
    END) / 1024 / 1024 AS [on-disk size MB]   
FROM sys.dm_db_xtp_checkpoint_files  
GROUP BY state, state_desc, file_type, file_type_desc  
ORDER BY state, file_type;

V případě počátečního stavu ukázky bude výsledek vypadat přibližně jako u serveru s 16 logickými procesory:

state_desc popis_typu_souboru počítat velikost disku MB
PŘEDEM VYTVOŘENO DATA 16 2048
PŘEDEM VYTVOŘENO DELTA 16 128
VE VÝSTAVBĚ DATA 1 128
VE VÝSTAVBĚ DELTA 1 8

Jak vidíte, většina místa je využívána předem vytvořenými daty a rozdílovými soubory. SQL Server předem vytvořil jeden pár souborů (data, rozdílové) na každý logický procesor. Datové soubory jsou navíc předem nastaveny na 128 MB a rozdílové soubory na 8 MB, aby se vkládání dat do těchto souborů zefektivnilo.

Skutečná data v tabulkách optimalizovaných pro paměť jsou v jednom datovém souboru.

Po spuštění úlohy

Po jednom testovacím spuštění, které vloží 10 milionů prodejních objednávek, vypadá celková velikost na disku přibližně takto (pro 16jádrový testovací server):

SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]  
FROM sys.filegroups f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX';  
Velikost na disku v MB
8828

Velikost na disku je blízko 9 GB, která se blíží velikosti dat v paměti.

Podrobněji se podíváme na velikosti souborů kontrolních bodů v různých státech:

SELECT state_desc  
 , file_type_desc  
 , COUNT(*) AS [count]  
 , SUM(CASE  
   WHEN state = 5 AND file_type=0 THEN 128*1024*1024  
   WHEN state = 5 AND file_type=1 THEN 8*1024*1024  
   WHEN state IN (6,7) THEN 68*1024*1024  
   ELSE file_size_in_bytes  
    END) / 1024 / 1024 AS [on-disk size MB]   
FROM sys.dm_db_xtp_checkpoint_files  
GROUP BY state, state_desc, file_type, file_type_desc  
ORDER BY state, file_type;
popis_stavu Popis typu souboru počítat velikost disku MB
PŘEDVYTVOŘENO DATA 16 2048
PŘEDPŘIPRAVENO DELTA 16 128
VE VÝSTAVBĚ DATA 1 128
VE VÝSTAVBĚ DELTA 1 8

Stále máme 16 dvojic předem vytvořených souborů, připravené k přechodu, protože kontrolní body jsou zavřené.

Existuje jeden pár při výstavbě, který je používán, dokud není uzavřen aktuální kontrolní bod. Společně s aktivními kontrolními soubory to poskytuje přibližně 6,5 GB využití disku pro 6,5 GB dat v paměti. Vzpomeňte si, že indexy nejsou trvalé na disku, a proto je celková velikost disku menší než velikost v paměti v tomto případě.

Po ukázkovém resetu

Po ukázkovém resetování se místo na disku okamžitě neuvolní, pokud v systému neexistuje žádná transakční úloha a neexistují žádné kontrolní body databáze. Aby se soubory kontrolních bodů postupně přesunuly skrz různé fáze a nakonec byly odstraněny, je potřeba provést řadu operací kontrolních bodů a událostí zkrácení protokolu, které zahájí sloučení těchto souborů, a zároveň iniciovat proces uvolňování nepotřebných dat. K těmto zátěžím dojde automaticky, pokud máte v systému transakční zátěž (a pravidelně zálohujete transakční logy, pokud používáte model ÚPLNÉ obnovení), ale ne, když je systém nečinný, jako je tomu v ukázkovém scénáři.

V příkladu po ukázkovém resetování se může zobrazit něco takového:

SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]  
FROM sys.filegroups f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX';
Velikost na disku v MB
11839

Téměř 12 GB je to výrazně větší než 9 GB, které jsme měli před obnovením ukázky. Důvodem je to, že byla zahájena některá sloučení souborů kontrolních bodů, ale některé cíle sloučení ještě nebyly nainstalovány a některé zdrojové soubory sloučení ještě nebyly vyčištěny, jak je vidět z následujícího:

SELECT state_desc  
 , file_type_desc  
 , COUNT(*) AS [count]  
 , SUM(CASE  
   WHEN state = 5 AND file_type=0 THEN 128*1024*1024  
   WHEN state = 5 AND file_type=1 THEN 8*1024*1024  
   WHEN state IN (6,7) THEN 68*1024*1024  
   ELSE file_size_in_bytes  
    END) / 1024 / 1024 AS [on-disk size MB]   
FROM sys.dm_db_xtp_checkpoint_files  
GROUP BY state, state_desc, file_type, file_type_desc  
ORDER BY state, file_type;
state_desc popis_typu_souboru počítat velikost disku MB
PŘEDEM VYTVOŘENO DATA 16 2048
PŘEDPŘIPRAVENO DELTA 16 128
AKTIVNÍ DATA 38 5152
AKTIVNÍ DELTA 38 1331
CÍL SLUČOVÁNÍ DATA 7 896
CÍL SLUČOVÁNÍ DELTA 7 56
SLOUČENÝ ZDROJ DATA 13 1772
SLOUČENÝ ZDROJ DELTA 13 455

Cíle sloučení jsou instalovány a sloučené zdroje jsou vyčištěny, jakmile v systému probíhá transakční aktivita.

Po druhém spuštění ukázkové úlohy, kdy po resetování ukázky vložíte 10 milionů prodejních objednávek, uvidíte, že soubory vytvořené během prvního spuštění úlohy byly vyčištěny. Pokud výše uvedený dotaz spustíte několikrát, když je úloha spuštěná, uvidíte, že soubory kontrolních bodů procházejí různými fázemi.

Po druhém spuštění úlohy vložíte 10 milionů prodejních objednávek, uvidíte velmi podobné využití disků, i když nemusí být nutně stejné jako po prvním spuštění, protože systém je dynamický v přírodě. Například:

SELECT state_desc  
 , file_type_desc  
 , COUNT(*) AS [count]  
 , SUM(CASE  
   WHEN state = 5 AND file_type=0 THEN 128*1024*1024  
   WHEN state = 5 AND file_type=1 THEN 8*1024*1024  
   WHEN state IN (6,7) THEN 68*1024*1024  
   ELSE file_size_in_bytes  
    END) / 1024 / 1024 AS [on-disk size MB]   
FROM sys.dm_db_xtp_checkpoint_files  
GROUP BY state, state_desc, file_type, file_type_desc  
ORDER BY state, file_type;
popis stavu popis_typu_souboru počítat velikost disku MB
PŘEDEM VYTVOŘENO Data 16 2048
PŘEDEM VYTVOŘENO DELTA 16 128
VE VÝSTAVBĚ DATA 2 268
VE VÝSTAVBĚ DELTA 2 16
AKTIVNÍ DATA 41 5608
AKTIVNÍ DELTA 41 328

V tomto případě jsou ve stavu "rozpracovaný" dva páry kontrolních souborů, což znamená, že do stavu "rozpracovaný" bylo přesunuto více párů souborů, pravděpodobně kvůli vysoké úrovni souběžnosti v zátěži. Několik souběžných vláken současně vyžadovalo nový pár souborů, a tím se jeden pár přesunul z předem vytvořených do fáze výstavby.

Další kroky