Sdílet prostřednictvím


Ukázková databáze pro OLTP v paměti

platí pro:SQL Serverazure SQL Database

Přehled

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

Poznámka

Chcete-li zobrazit tento článek pro SQL Server 2014 (12.x), přečtěte si Rozšíření AdventureWorks k demonstrování In-Memory OLTP.

Ukázka migruje pět tabulek v databázi AdventureWorks2025 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 při použití OLTP v paměti na vašem serveru.

V popisu ukázky probereme kompromisy, které byly provedeny při migraci tabulek do OLTP v paměti, aby se zohlednily funkce, které nejsou (ještě) podporované 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 OLTP v paměti najdete v následujícím blogovém příspěvku: Aspekty hardwaru pro In-Memory OLTP na SQL Serveru

Nainstalujte ukázku paměťového OLTP založenou na 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. Identifikujte cílovou složku a název souboru dat, například:

      H:\DATA\AdventureWorks2022_Data.mdf
      
    2. Identifikujte cílovou složku a název 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 OLTP\readme.txt .

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 AdventureWorks2025. Schéma nových tabulek je podobné existujícím tabulkám s několika rozdíly, jak je vysvětleno dále v této části.

Nové tabulky optimalizované pro paměť mají příponu _inmem. Ukázka také obsahuje odpovídající tabulky s příponou _ondisk – tyto tabulky lze 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 AdventureWorks2025ná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ěť

Nové tabulky zavedené touto ukázkou obvykle používají stejné sloupce a stejné datové typy jako původní tabulky. Existuje však několik rozdílů. Uvádíme rozdíly v této části 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 globální zdroj a zjistili jsme, že může představovat úzké hrdlo při spuštění úlohy In-Memory OLTP, 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 detaily 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 s možnou hodnotou null v klíčích indexu – v původní tabulce má sloupec SalesPersonID hodnotu null, zatímco v nových tabulkách sloupec nemá 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.

  • Vypočí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). Počínaje SQL Serverem 2017 (14.x) se počítané sloupce podporují v tabulkách a indexech optimalizovaných pro paměť.
  • 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 SalesOrderHeader_inmem je horká tabulka v ukázkové úloze a omezení cizích klíčů vyžadují dodatečné 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. Proto předpokládáme, že aplikace zajistí referenční integritu Sales.SalesOrderHeader_inmem tabulky a při vložení řádků se neověří referenční integrita.

  • Sloupec rowguid je vynechán. I když se u tabulek optimalizovaných pro paměť podporuje uniqueidentifier, možnost ROWGUIDCOL není v SQL Serveru 2016 (13.x) podporovaná. 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 SalesOrderHeadervýchozí omezení vyžadující systémové datum a čas se nemigruje. Místo toho se uložená procedura, která vkládá prodejní objednávky, 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é v tabulkách 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é skenování nebo vyhledávání na vedoucích sloupcích klíče 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. Poskytnutá datová sada nemá mnoho obchodních zástupců. 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 index 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 HASH indexy kvůli výkonu a protože pro úlohu nejsou potřeba žádné seřazené skeny ani skeny rozsahu.

  • Index HASH pro (SalesOrderID, SalesOrderDetailID): toto je index primárního klíče, a i když vyhledávání (SalesOrderID, SalesOrderDetailID) jsou občasné, použití indexu hash 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 je časté: Chcete 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ů).

  • HASH index na (ProductID): bucket_count je 1 milion. Zadaná datová sada nemá mnoho 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.

  • NONCLUSTERED index on (Name): To umožňuje uspořádané prohledávání názvů produktů.

  • NEKLASTROVANÝ index na (ProductNumber): To umožňuje uspořádané procházení čí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 není zmíněn v ukázkové zátěži, a proto není zřejmá potřeba použít hash indexy pro optimalizaci této tabulky – indexy na (SpecialOfferID, ProductID) a (ProductID) jsou neklastrové.

V předchozím příkladu jsou některé počty záznamů nadměrné, ale ne počty záznamů pro indexy SalesOrderHeader_inmem a SalesOrderDetail_inmem: ty jsou určeny 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 chybou nedostatku 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é vzorkem

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 řá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é):

      • @DueDatedatetime2
      • @CustomerIDint
      • @BillToAddressIDint
      • @ShipToAddressIDint
      • @ShipMethodIDint
      • @SalesOrderDetailsSales.SalesOrderDetailType_inmem - parametr hodnotového typu tabulky (TVP) který obsahuje řádkové položky objednávky
    • Vstupní parametry (volitelné):

      • @Statustinyint
      • @OnlineOrderFlagbit
      • @PurchaseOrderNumbernvarchar(25)
      • @AccountNumbernvarchar(15)
      • @SalesPersonIDint
      • @TerritoryIDint
      • @CreditCardIDint
      • @CreditCardApprovalCodevarchar(15)
      • @CurrencyRateIDint
      • @Commentnvarchar(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í. Je určena k volání z obalové uložené procedury 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 AdventureWorks2025.

    • 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 roztáčí vlákna a spustí příkaz na všech vláknech paralelně. Po ukončení provádění všech vláken ostress oznámí čas potřebný k dokončení provádění všech vláken.

Nainstalujte 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 se zobrazí dialogové okno s informací, že se některé soubory používají, vyberte Pokračovat.

Spustit ostress

Ostress se spouští z příkazového řádku. Nejpohodlnější je spustit nástroj z příkazového řádku RML, který je nainstalovaný 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říkazový řádek RML, který je 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 lze zobrazit, když jednoduše běží ostress.exe bez možností příkazového řádku. Hlavní možnosti, které je vhodné zvážit při spuštění ostress s touto ukázkou , jsou následující:

Možnost Description
-S Název instance SQL Serveru, ke které se chcete připojit.
-E Použití ověřování systému Windows k připojení (výchozí); pokud používáte ověřování SQL Serveru, použijte možnosti -U a -P zadejte uživatelské jméno a heslo.
-d Název databáze, v tomto příkladu AdventureWorks2025.
-Q Příkaz 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í ke spuštění každého vstupního souboru nebo 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 s tabulkovou hodnotou (TVP) s příkladovými daty a zavolá proceduru pro vložení prodejní objednávky s pěti položkami na řádcích.

Nástroj ostress slouží k paralelnímu spouštění volání uložených procedur k simulaci klientů vkládajících prodejní objednávky souběžně.

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 AS INT = 0, @od AS Sales.SalesOrderDetailType_inmem, @SalesOrderID AS INT, @DueDate AS DATETIME2 = sysdatetime(), @CustomerID AS INT = RAND() * 8000, @BillToAddressID AS INT = RAND() * 10000, @ShipToAddressID AS INT = RAND() * 10000, @ShipMethodID AS 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
        EXECUTE 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 vytváří prodejní objednávku k vložení.

Předchozí 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 s _ondisk.

Pomocí nástroje ostress spustíme skripty pomocí několika souběžných připojení. Tento parametr -n používáme k řízení počtu připojení a parametru r , abychom mohli řídit, kolikrát se skript provádí na každém připojení.

Spustit úlohu

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 nefunguje dobře, projděte si čá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, jak je hlášeno 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í úlohy s omezeným zatížením, které vkládají 1 milion prodejních objednávek, najdete v instrukcích v In-Memory OLTP\readme.txt, které jsou součástí archivu 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 AS INT = 0, @od AS Sales.SalesOrderDetailType_inmem, @SalesOrderID AS INT, @DueDate AS DATETIME2 = SYSDATETIME(), @CustomerID AS INT = RAND() * 8000, @BillToAddressID AS INT = RAND() * 10000, @ShipToAddressID AS INT = RAND() * 10000, @ShipMethodID AS 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 EXECUTE 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. Vidíte, že využití procesoru je blízko 100%. Pokud tomu tak není, máte úzké hrdlo u protokolových vstupně-výstupních operací, 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 discích. Spuštění této úlohy může chvíli trvat, což je do velké míry způsobeno kolizemi synchronizačních zámků v systému. Tabulky optimalizované pro paměť jsou bez západky, 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 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 AS INT = 0, @od AS Sales.SalesOrderDetailType_ondisk, @SalesOrderID AS INT, @DueDate AS DATETIME2 = sysdatetime(), @CustomerID AS INT = RAND() * 8000, @BillToAddressID AS INT = RAND() * 10000, @ShipToAddressID AS INT = RAND() * 10000, @ShipMethodID AS 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 EXECUTE 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 to, ž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 řádky dat nejsou uspořádané na stránkách. Souběžné transakce tedy navzájem neblokují vložení, což umožňuje SQL Serveru plně využívat 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 je využití procesoru 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í demoverze

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. Vzhledem k tomu, že tato úloha je pouze vkládací, každé spuštění spotřebovává více paměti, a proto je nutné resetovat, aby se předešlo nedostatku 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í problémů s pomalu běžící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 očekávané, podívejte se na několik věcí:

  • Počet souběžných transakcí: Při spouštění úlohy v jednom vlákně je zvýšení výkonu s In-Memory OLTP pravděpodobně menší než 2X. Kolize západek je pouze významným problémem, pokud existuje vysoká úroveň souběžnosti.

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

    • Příznak: Pokud je vysoké využití procesoru při spouštění úloh na tabulkách založených na disku, znamená to, že je malý prostor pro kolize, což poukazuje na nedostatek souběžnosti.
  • Rychlost jednotky protokolu: Pokud jednotka protokolu nemůže držet krok s úrovní propustnosti transakcí v systému, dochází ke zúžení výkonu na 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í CPU není blízko 100% nebo je při spouštění úloh na tabulkách optimalizovaných pro paměť velmi kolísavé, může docházet k úzkému místu v logu IO. 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. Zobrazíme také výsledky 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, v tomto případě databázi s ID 5 (tento database_id se může lišit ve vašem vlastním případu), je asi 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 AS dms
     INNER JOIN sys.tables AS 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 memory_allocated_for_table_kb memory_allocated_for_indexes_kb
SpecialOfferProduct_inmem 64 3840
DemoSalesOrderHeaderSeed 1984 5504
SalesOrderDetail_inmem 15316 663552
DemoSalesOrderDetailSeed 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 nastaveny pro větší velikost dat. Indexy hash mají pevnou velikost, a proto se jejich velikost nezvětší s velikostí dat v tabulce.

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

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

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

Tady je soubor výsledků.

type name pages_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 AS dms
     INNER JOIN sys.tables AS t
         ON dms.object_id = t.object_id
WHERE t.type = 'U';

Tady je soubor výsledků.

Table name memory_allocated_for_table_kb memory_allocated_for_indexes_kb
SalesOrderDetail_inmem 5113761 663552
DemoDetailProdejníObjednávkySemínko 64 10368
SpecialOffer_inmem 2 8192
SalesOrderHeader_inmem 1575679 147456
Product_inmem 111 12032
SpeciálníNabídkaProdukt_inmem 64 3712
DemoSalesOrderHeaderSeed 1984 5504

Vidíme celkem přibližně 6,5 GB dat. 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_inmemznovu uloží 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í. Vidíte, že okamžitě po demo resetu, kdy na systému není žádná transakční úloha, 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%';

Tady je soubor výsledků.

type name pages_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ěť se uvolní, když je spuštěná transakční úloha.

Pokud spustíte druhé spuštění ukázkové úlohy, zpočátku se využití paměti sníží, protože dříve odstraněné řádky jsou odstraněny. V určitém okamžiku se velikost paměti znovu zvýší, dokud se úloha nedokončí. Po vložení 10 milionů řádků po ukázkovém resetování je 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%';

Tady je soubor výsledků.

type name pages_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 AS f
     INNER JOIN sys.database_files AS df
         ON f.data_space_id = df.data_space_id
WHERE f.type = N'FX';

Počáteční stav

Když se zpočátku vytvoří ukázková skupina souborů a ukázkové tabulky optimalizované pro paměť, vytvoří se několik souborů kontrolních bodů a systém začne vyplňovat 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 jsou 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 AS f
     INNER JOIN sys.database_files AS df
         ON f.data_space_id = df.data_space_id
WHERE f.type = N'FX';

Tady je soubor výsledků.

Velikost na disku v MB
2312

Jak vidíte, mezi velikostí souborů kontrolního bodu na disku, což je 2,3 GB, a skutečnou velikostí datových souborů, která je blíže 30 MB, je velká nesrovnalost.

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 vypadá výsledek přibližně jako následující tabulka pro server 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, delta) na logický procesor. Datové soubory jsou navíc předem nastavené na 128 MB a rozdílové soubory na 8 MB pro efektivnější vkládání dat do těchto souborů.

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 AS f
     INNER JOIN sys.database_files AS df
         ON f.data_space_id = df.data_space_id
WHERE f.type = N'FX';

Tady je soubor výsledků.

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;

Tady je soubor výsledků.

state_desc file_type_desc count on-disk size 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

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

Existuje jeden pár ve výstavbě, který se používá, dokud nebude 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 se neuchovávají na disku, a proto je celková velikost disku menší než velikost v paměti v tomto případě.

Po resetování demo

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ů přesunuly skrze různé fáze a nakonec byly zahozeny, je potřeba provést několik kontrolních bodů a událostí zkrácení protokolu, zahájit sloučení souborů kontrolních bodů a rovněž zahájit odklízení nepotřebných dat. K těmto událostem dochází automaticky, pokud máte v systému transakční pracovní zátěž (a pravidelně zálohujete logy, v případě že používáte model úplného obnovení), ale nedochází k nim, když je systém ve stavu nečinnosti, jako například při 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 AS f
     INNER JOIN sys.database_files AS df
         ON f.data_space_id = df.data_space_id
WHERE f.type = N'FX';

Tady je soubor výsledků.

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 bylo zahájeno sloučení některých kontrolních souborů, ale některé cíle sloučení ještě nejsou nainstalovány a některé zdrojové soubory sloučení ještě nebyly odstraněny, jak je vidět v následujícím příkladu:

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;

Tady je soubor výsledků.

state_desc file_type_desc count on-disk size MB
PŘEDEM VYTVOŘENO DATA 16 2048
PŘEDEM VYTVOŘENO 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 a vložení 10 milionů prodejních objednávek po resetování ukázky zjistíte, že soubory vytvořené během prvního spuštění úlohy byly odstraněny. Pokud předchozí 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, kdy bylo vloženo 10 milionů prodejních objednávek, se využití disků velmi podobá, i když nemusí být nutně stejné jako po prvním spuštění, protože systém je dynamický. 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;

Tady je soubor výsledků.

state_desc file_type_desc count on-disk size 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 UNDER CONSTRUCTION stavu dva páry kontrolních bodů, což znamená, že do UNDER CONSTRUCTION stavu bylo přesunuto více párů souborů, pravděpodobně kvůli vysoké úrovni souběžnosti v úloze. Několik souběžných vláken vyžadovalo současně novou dvojici souborů a tím se přesunul pár z PRECREATED do UNDER CONSTRUCTION.