Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
              gäller för:SQL Server
Azure SQL Database
Överblick
Det här exemplet visar den minnesinterna OLTP-funktionen. Den visar minnesoptimerade tabeller och inbyggda kompilerade lagrade procedurer och kan användas för att demonstrera prestandafördelar med minnesintern OLTP.
Notera
Om du vill se den här artikeln för SQL Server 2014 (12.x), se Tillägg till AdventureWorks för att demonstrera In-Memory OLTP.
Exemplet migrerar fem tabeller i AdventureWorks2022-databasen till minnesoptimerad och innehåller en demoarbetsbelastning för bearbetning av försäljningsorder. Du kan använda den här demoarbetsbelastningen för att se prestandafördelarna med att använda minnesintern OLTP på servern.
I beskrivningen av exemplet diskuterar vi de kompromisser som gjordes när tabellerna migrerades till minnesintern OLTP för att ta hänsyn till de funktioner som inte (ännu) stöds för minnesoptimerade tabeller.
Dokumentationen för det här exemplet är strukturerad på följande sätt:
- Förutsättningar för att installera exemplet och köra demoarbetsbelastningen. 
- Instruktioner för Att installera In-Memory OLTP-exempel baserat på AdventureWorks. 
- Beskrivning av exempeltabeller och procedurer – innehåller beskrivningar av tabeller och procedurer som lagts till - AdventureWorks2022i det minnesinterna OLTP-exemplet, samt överväganden för migrering av några av de ursprungliga- AdventureWorks2022tabellerna som ska minnesoptimeras.
- Instruktioner för att utföra prestandamätningar med demoarbetsbelastningen – innehåller instruktioner för att installera och köra ostress, ett verktyg som använder för att köra arbetsbelastningen och köra själva demoarbetsbelastningen. 
Förutsättningar
- 
SQL Server 2016 (13.x) 
- För prestandatestning, en server med specifikationer som liknar din produktionsmiljö. För det här exemplet bör du ha minst 16 GB minne tillgängligt för SQL Server. Allmänna riktlinjer för maskinvara för minnesintern OLTP finns i följande blogginlägg: Maskinvaruöverväganden för In-Memory OLTP i SQL Server 
Installera det minnesinterna OLTP-exemplet baserat på AdventureWorks
Följ de här stegen för att installera exemplet:
- Ladda ned - AdventureWorks2016_EXT.bakoch- SQLServer2016Samples.zipfrån: https://github.com/microsoft/sql-server-samples/releases/tag/adventureworks till en lokal mapp, till exempel- C:\Temp.
- Återställ databassäkerhetskopian med hjälp av Transact-SQL eller SQL Server Management Studio: - Identifiera målmappen och filnamnet för datafilen, till exempel: - H:\DATA\AdventureWorks2022_Data.mdf
- Identifiera målmappen och filnamnet för loggfilen, till exempel: - I:\DATA\AdventureWorks2022_log.ldf- Loggfilen ska placeras på en annan enhet än datafilen, helst en enhet med låg svarstid, till exempel en SSD- eller PCIe-lagring, för maximal prestanda.
 
 - Exempel på T-SQL-skript: - 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
- Om du vill visa exempelskripten och arbetsbelastningen packar du upp filen - SQLServer2016Samples.ziptill en lokal mapp. Konsultera filen- In-Memory OLTP\readme.txtför instruktioner om hur du kör arbetslasten.
Beskrivning av exempeltabeller och procedurer
Exemplet skapar nya tabeller för produkter och försäljningsorder baserat på befintliga tabeller i AdventureWorks2022. Schemat för de nya tabellerna liknar de befintliga tabellerna, med några skillnader, som beskrivs senare i det här avsnittet.
De nya minnesoptimerade tabellerna har suffixet _inmem. Exemplet innehåller även motsvarande tabeller med suffixet _ondisk – dessa tabeller kan användas för att göra en en-till-en-jämförelse mellan prestanda för minnesoptimerade tabeller och diskbaserade tabeller i systemet.
De minnesoptimerade tabeller som används i arbetsbelastningen för prestandajämförelse är helt hållbara och fullständigt loggade. De offrar inte hållbarhet eller tillförlitlighet för att uppnå prestandavinsten.
Målarbetsbelastningen för det här exemplet är bearbetning av försäljningsorder, där vi även överväger information om produkter och rabatter. Därför använder vi tabellerna SalesOrderHeader, SalesOrderDetail, Product, SpecialOfferoch SpecialOfferProduct.
Två nya lagrade procedurer, Sales.usp_InsertSalesOrder_inmem och Sales.usp_UpdateSalesOrderShipInfo_inmem, används för att infoga försäljningsorder och uppdatera leveransinformationen för en viss försäljningsorder.
Det nya schemat Demo innehåller hjälptabeller och lagrade procedurer för att köra en demoarbetsbelastning.
Konkret lägger In-Memory OLTP-exemplet till följande objekt i AdventureWorks2022:
Tabeller som lagts till av exemplet
De nya tabellerna
Sales.SalesOrderHeader_inmem
- Rubrikinformation om försäljningsorder. Varje försäljningsorder har en rad i den här tabellen.
Sales.SalesOrderDetail_inmem
- Information om försäljningsorder. Varje radobjekt i en försäljningsorder har en rad i den här tabellen.
Sales.SpecialOffer_inmem
- Information om specialerbjudanden, inklusive rabattprocenten som är associerad med varje specialerbjudande.
Sales.SpecialOfferProduct_inmem
- Referenstabell mellan specialerbjudanden och produkter. Varje specialerbjudande kan innehålla noll eller fler produkter, och varje produkt kan presenteras i noll eller fler specialerbjudanden.
Production.Product_inmem
- Information om produkter, inklusive deras listpris.
Demo.DemoSalesOrderDetailSeed
- Används i demobelastningen för att skapa exempelförsäljningsorder.
Diskbaserade varianter av tabellerna:
- Sales.SalesOrderHeader_ondisk
- Sales.SalesOrderDetail_ondisk
- Sales.SpecialOffer_ondisk
- Sales.SpecialOfferProduct_ondisk
- Production.Product_ondisk
Skillnader mellan ursprungliga diskbaserade och nya minnesoptimerade tabeller
Vanligtvis använder de nya tabeller som introduceras i det här exemplet samma kolumner och samma datatyper som de ursprungliga tabellerna. Det finns dock några skillnader. Vi listar skillnaderna i det här avsnittet tillsammans med en motivering för ändringarna.
Sales.SalesOrderHeader_inmem
- Standardbegränsningar stöds för minnesoptimerade tabeller, och de flesta standardbegränsningar migrerade vi som de är. Den ursprungliga tabellen - Sales.SalesOrderHeaderinnehåller dock två standardbegränsningar som hämtar det aktuella datumet, för kolumnerna- OrderDateoch- ModifiedDate. I en orderbearbetningsarbetsbelastning med högt dataflöde med mycket samtidighet kan alla globala resurser bli en konkurrensfråga. Systemtiden är en global resurs och vi har observerat att det kan bli en flaskhals när du kör ett In-Memory OLTP-jobb som infogar försäljningsorder, speciellt om systemtiden behöver hämtas för flera kolumner i försäljningsorderhuvudet och försäljningsorderdetaljerna. Problemet åtgärdas i det här exemplet genom att bara hämta systemtiden en gång för varje försäljningsorder som infogas och använda det värdet för datetime-kolumnerna i- SalesOrderHeader_inmemoch- SalesOrderDetail_inmemi den lagrade proceduren- Sales.usp_InsertSalesOrder_inmem.
- Användardefinierade datatyper för alias (UDT) – Den ursprungliga tabellen använder två alias-UDT:er - dbo.OrderNumberoch- dbo.AccountNumberför kolumnerna- PurchaseOrderNumberrespektive- AccountNumber. SQL Server 2016 (13.x) stöder inte alias-UDT för minnesoptimerade tabeller. Därför använder de nya tabellerna systemdatatyperna nvarchar(25) respektive nvarchar(15).
- Nullbara kolumner i indexnycklar – I den ursprungliga tabellen är kolumnen - SalesPersonIDnull, medan kolumnen i de nya tabellerna inte är nullbar och har en standardbegränsning med värdet (-1). Den här omständigheten beror på att index i minnesoptimerade tabeller inte kan ha nullbara kolumner i indexnyckeln. -1 är surrogat för NULL i det här fallet.
- Beräknade kolumner – De beräknade kolumnerna - SalesOrderNumberoch- TotalDueutelämnas, eftersom SQL Server 2016 (13.x) inte stöder beräknade kolumner i minnesoptimerade tabeller. Den nya vyn- Sales.vSalesOrderHeader_extended_inmemåterspeglar kolumnerna- SalesOrderNumberoch- TotalDue. Därför kan du använda den här vyn om dessa kolumner behövs.- Gäller för: SQL Server 2017 (14.x). Från och med SQL Server 2017 (14.x) stöds beräknade kolumner i minnesoptimerade tabeller och index.
 
- Främmande nyckelbegränsningar stöds för minnesoptimerade tabeller i SQL Server 2016 (13.x), men endast om de refererade tabellerna också är minnesoptimerade. Sekundärnycklar som refererar till tabeller som också migreras till minnesoptimerade sparas i de migrerade tabellerna, medan andra sekundärnycklar utelämnas. Dessutom är - SalesOrderHeader_inmemen het tabell i exempelarbetsbelastningen, och begränsningar för främmande nycklar kräver extra bearbetning för alla DML-åtgärder, eftersom uppslag krävs i alla andra tabeller som dessa begränsningar refererar till. Därför är antagandet att appen säkerställer referensintegriteten för- Sales.SalesOrderHeader_inmemtabellen och att referensintegriteten inte verifieras när rader infogas.
- Rowguid – Kolumnen rowguid utelämnas. Även om uniqueidentifier stöds för minnesoptimerade tabeller stöds inte alternativet ROWGUIDCOL i SQL Server 2016 (13.x). Kolumner av den här typen används vanligtvis för antingen sammanslagningsreplikering eller tabeller som har filströmskolumner. Det här exemplet innehåller ingetdera. 
Försäljning.Försäljningsorderdetalj
- Standardbegränsningar - liknande - SalesOrderHeader, migreras inte standardbegränsningen som kräver systemets datum/tid. I stället tar den lagrade proceduren som infogar försäljningsorder hand om att infoga det aktuella systemets datum/tid vid första infogningen.
- Beräknade kolumner – den beräknade kolumnen - LineTotalmigrerades inte eftersom beräknade kolumner inte stöds med minnesoptimerade tabeller i SQL Server 2016 (13.x). Använd- Sales.vSalesOrderDetail_extended_inmem-vyn för att komma åt den här kolumnen.
- Rowguid – kolumnen - rowguidutelämnas. Mer information finns i beskrivningen för tabellen- SalesOrderHeader.
Produktion.Produkt
- alias-UDT:er – den ursprungliga tabellen använder den användardefinierade datatypen - dbo.Flag, vilket motsvarar biten för systemdatatypen. Den migrerade tabellen använder bitdatatypen i stället.
- Rowguid – kolumnen - rowguidutelämnas. Mer information finns i beskrivningen för tabellen- SalesOrderHeader.
Försäljning.Specialerbjudande
- 
              Rowguid – kolumnen rowguidutelämnas. Mer information finns i beskrivningen för tabellenSalesOrderHeader.
Försäljning.SPECIALERBJUDANDEPRODUKT
- 
              Rowguid – kolumnen rowguidutelämnas. Mer information finns i beskrivningen för tabellenSalesOrderHeader.
Överväganden för index i minnesoptimerade tabeller
Baslinjeindexet för minnesoptimerade tabeller är NONCLUSTERED-indexet, som stöder punktsökningar (indexsökning på likhetspredikat), intervallgenomsökningar (index söker efter ojämlikhetspredikat), fullständiga indexgenomsökningar och ordnade genomsökningar. Dessutom stöder NONCLUSTERED-index sökning på inledande kolumner i indexnyckeln. Minnesoptimerade NONCLUSTERED-index stöder faktiskt alla åtgärder som stöds av diskbaserade NONCLUSTERED-index, med det enda undantaget är bakåtgenomsökningar. Därför är det säkert att använda NONCLUSTERED-index för dina index.
HASH-index kan användas för att ytterligare optimera arbetsbelastningen. De är optimerade för punktuppslag och radinsättningar. Man måste dock tänka på att de inte stöder intervallgenomsökningar, ordnade genomsökningar eller sökning på ledande indexnyckelkolumner. Därför måste du vara försiktig när du använder dessa index. Dessutom är det nödvändigt att ange bucket_count vid skapandetillfället. Det bör vanligtvis anges till mellan en och två gånger så många indexnyckelvärden, men överskattning är vanligtvis inte ett problem.
Mer information finns i:
Indexen på de migrerade tabellerna optimerades med hänsyn till demoförsäljningsorderbearbetningens arbetsbelastning. Arbetsbelastningen förlitar sig på infogningar och punktsökningar i tabellerna Sales.SalesOrderHeader_inmem och Sales.SalesOrderDetail_inmemoch förlitar sig även på punktsökningar på primärnyckelkolumnerna i tabellerna Production.Product_inmem och Sales.SpecialOffer_inmem.
              Sales.SalesOrderHeader_inmem har tre index, som alla är HASH-index av prestandaskäl, och eftersom inga sorteringar eller intervallgenomsökningar behövs för arbetsbelastningen.
- HASH-index på ( - SalesOrderID): bucket_count har en storlek på 10 miljoner (avrundat upp till 16 miljoner), eftersom det förväntade antalet försäljningsorder är 10 miljoner
- HASH-index på ( - SalesPersonID): bucket_count är 1 miljon. Den angivna datamängden har inte många säljare. Men denna stora bucket_count möjliggör framtida tillväxt. Dessutom betalar du inte en prestandaavgift för poängsökningar om bucket_count är överdimensionerad.
- HASH-index på ( - CustomerID): bucket_count är 1 miljon. Den angivna datamängden har inte många kunder, men detta möjliggör framtida tillväxt.
              Sales.SalesOrderDetail_inmem har tre index, som alla är HASH-index av prestandaskäl, och eftersom inga sorteringar eller intervallgenomsökningar behövs för arbetsbelastningen.
- HASH-index på ( - SalesOrderID,- SalesOrderDetailID): det här är primärnyckelindexet, och även om sökningarna på (- SalesOrderID,- SalesOrderDetailID) är ovanliga, med hjälp av ett hash-index för nyckeln påskyndas radinfogningar. Bucket_count är satt till 50 miljoner (avrundat upp till 67 miljoner); det förväntade antalet försäljningsorder är 10 miljoner, och denna är dimensionerad för att ha ett genomsnitt på fem artiklar per order.
- HASH-index på ( - SalesOrderID): sökningar efter försäljningsorder är vanliga: du vill hitta alla radobjekt som motsvarar en enskild order. bucket_count är storleksanpassat till 10 miljoner (avrundat upp till 16 miljoner), eftersom det förväntade antalet försäljningsorder är 10 miljoner
- HASH-index på ( - ProductID): bucket_count är 1 miljon. Den angivna datamängden har inte många produkter, men detta möjliggör framtida tillväxt.
              Production.Product_inmem har tre index
- HASH-index på ( - ProductID): sökningar på- ProductIDligger i den kritiska vägen för demobelastningen, därför är detta ett hash-index.
- NONCLUSTERED-index på ( - Name): detta tillåter ordnade genomsökningar av produktnamn
- NONCLUSTERED-index på ( - ProductNumber): detta tillåter ordnade genomsökningar av produktnummer
              Sales.SpecialOffer_inmem har ett HASH-index på (SpecialOfferID): punktsökningar av specialerbjudanden finns i den kritiska delen av demoarbetsbelastningen. 
              bucket_count har en storlek på 1 miljon för att möjliggöra framtida tillväxt.
              Sales.SpecialOfferProduct_inmem refereras inte till i demoarbetsbelastningen och därför finns det inget uppenbart behov av att använda hash-index i den här tabellen för att optimera arbetsbelastningen – indexen på (SpecialOfferID, ProductID) och (ProductID) är NONCLUSTERED.
I föregående exempel är några av bucketantalen överdimensionerade, men inte bucketantalet för indexen på SalesOrderHeader_inmem och SalesOrderDetail_inmem: de är storleksanpassade för bara 10 miljoner försäljningsorder. Detta gjordes för att tillåta installation av exemplet på system med låg minnestillgänglighet, men i dessa fall misslyckas demouppgiften med ett minnesfel. Om du vill skala långt över 10 miljoner försäljningsorder kan du öka antalet bucketar i enlighet med detta.
Överväganden för minnesanvändning
Minnesanvändning i exempeldatabasen, både före och efter körning av demoarbetsbelastningen, beskrivs i avsnittet Minnesanvändning för de minnesoptimerade tabellerna.
Procedurer lagrade som har lagts till av exempelprogrammet
De två viktiga lagrade procedurerna för att infoga försäljningsorder och uppdatera leveransinformationen är följande:
- Sales.usp_InsertSalesOrder_inmem- Infogar en ny försäljningsorder i databasen och matar ut - SalesOrderIDför försäljningsordern. Som indataparametrar tar den information om försäljningsorderrubriken och radobjekten i ordern.
- Utdataparameter: - 
              @SalesOrderID int – SalesOrderIDför försäljningsordern som just infogades
 
- 
              @SalesOrderID int – 
- Indataparametrar (krävs): - @DueDatedatetime2
- @CustomerIDint
- @BillToAddressIDint
- @ShipToAddressIDint
- @ShipMethodIDint
- 
              
              
              Sales.SalesOrderDetailType_inmem@SalesOrderDetails – tabellvärdesparameter (TVP) som innehåller radobjekten i ordern
 
- Indataparametrar (valfritt): - @Statustinyint
- @OnlineOrderFlagbit
- @PurchaseOrderNumbernvarchar(25)
- @AccountNumbernvarchar(15)
- @SalesPersonIDint
- @TerritoryIDint
- @CreditCardIDint
- @CreditCardApprovalCodevarchar(15)
- @CurrencyRateIDint
- @Commentnvarchar(128)
 
 
- Sales.usp_UpdateSalesOrderShipInfo_inmem- Uppdatera leveransinformationen för en viss försäljningsorder. Detta uppdaterar även leveransinformationen för alla radartiklar i försäljningsordern. 
- Det här är en omslutningsprocedur för inbyggda kompilerade lagrade procedurer - Sales.usp_UpdateSalesOrderShipInfo_nativemed logik för återförsök för att hantera (oväntade) potentiella konflikter med samtidiga transaktioner som uppdaterar samma ordning. Mer information finns i omförsökslogik.
 
- Sales.usp_UpdateSalesOrderShipInfo_native- Det här är den internt kompilerade lagrade proceduren som faktiskt bearbetar uppdateringen av leveransinformationen. Det är tänkt att anropas från den lagrade Wrapper-proceduren Sales.usp_UpdateSalesOrderShipInfo_inmem. Om klienten kan hantera fel och implementerar logik för återförsök kan du anropa den här proceduren direkt i stället för att använda den omslutna lagrade proceduren.
 
- Det här är den internt kompilerade lagrade proceduren som faktiskt bearbetar uppdateringen av leveransinformationen. Det är tänkt att anropas från den lagrade Wrapper-proceduren 
Följande lagrade procedur används för demoarbetsbelastningen.
- Demo.usp_DemoReset- Återställer demon genom att tömma och påfylla tabellerna SalesOrderHeaderochSalesOrderDetail.
 
- Återställer demon genom att tömma och påfylla tabellerna 
Följande lagrade procedurer används för att infoga och ta bort från minnesoptimerade tabeller samtidigt som domän och referensintegritet garanteras.
- Production.usp_InsertProduct_inmem
- Production.usp_DeleteProduct_inmem
- Sales.usp_InsertSpecialOffer_inmem
- Sales.usp_DeleteSpecialOffer_inmem
- Sales.usp_InsertSpecialOfferProduct_inmem
Slutligen används följande lagrade procedur för att verifiera domän och referensintegritet.
- dbo.usp_ValidateIntegrity- Valfri parameter: @object_id – ID för objektet för att verifiera integriteten för 
- Den här proceduren förlitar sig på tabellerna - dbo.DomainIntegrity,- dbo.ReferentialIntegrityoch- dbo.UniqueIntegrityför de integritetsregler som måste verifieras . Exemplet fyller i dessa tabeller baserat på de kontroll-, sekundärnyckel- och unika begränsningar som finns för de ursprungliga tabellerna i- AdventureWorks2022-databasen.
- Den förlitar sig på hjälpprocedurerna - dbo.usp_GenerateCKCheck,- dbo.usp_GenerateFKCheckoch- dbo.GenerateUQCheckför att generera den T-SQL som behövs för att utföra integritetskontrollerna.
 
Prestandamätningar med demoarbetsbelastningen
ostress är ett kommandoradsverktyg som har utvecklats av microsoft CSS SQL Server-supportteamet. Det här verktyget kan användas för att köra frågor eller köra lagrade procedurer parallellt. Du kan konfigurera antalet trådar för att köra en viss T-SQL-instruktion parallellt, och du kan ange hur många gånger instruktionen ska köras på den här tråden. ostress snurrar upp trådarna och kör instruktionen på alla trådar parallellt. När körningen är klar för alla trådar rapporterar ostress hur lång tid det tog för alla trådar att slutföra körningen.
Installera ostress
ostress installeras som en del av RML-verktyg (Report Markup Language). det finns ingen fristående installation för ostress.
Installationssteg:
- Ladda ned och kör x64-installationspaketet för RML-verktygen från följande sida: Ladda ned RML för SQL Server 
- Om det finns en dialogruta som säger att vissa filer används väljer du Fortsätt 
Kör ostress
Ostress körs från kommandotolken. Det är mest praktiskt att köra verktyget från RML-cmd prompten, som installeras som en del av RML-verktygen.
Om du vill öppna RML-cmd prompten följer du dessa instruktioner:
Öppna Start-menyn i Windows genom att välja Windows-nyckeln och skriv rml. Välj RML-cmd prompt, som finns i listan över sökresultat.
Kontrollera att kommandotolken finns i installationsmappen för RML-verktyg.
Kommandoradsalternativen för ostress visas när du bara kör ostress.exe utan kommandoradsalternativ. De viktigaste alternativen för att köra ostress med det här exemplet är följande:
| Option | Description | 
|---|---|
| -S | Namnet på DEN SQL Server-instans som ska anslutas till. | 
| -E | Använd Windows-autentisering för att ansluta (standard); Om du använder SQL Server-autentisering använder du alternativen -Uoch-Panger användarnamn respektive lösenord. | 
| -d | Namnet på databasen, för det här exemplet AdventureWorks2022. | 
| -Q | T-SQL-instruktionen som ska köras. | 
| -n | Antal anslutningar som bearbetar varje indatafil/fråga. | 
| -r | Antalet iterationer för varje anslutning för att köra varje indatafil/fråga. | 
Demoarbetsbelastning
Den huvudsakliga lagrade proceduren som används i demoarbetsbelastningen är Sales.usp_InsertSalesOrder_inmem/ondisk. Skriptet i följande exempel konstruerar en tabellvärdesparameter (TVP) med exempeldata och anropar proceduren för att infoga en försäljningsorder med fem radobjekt.
Ostress-verktyget används för att köra de lagrade proceduranropen parallellt för att simulera klienter som infogar försäljningsorder samtidigt.
Återställ demonstrationen efter varje stresskörning som körs Demo.usp_DemoReset. Den här proceduren tar bort raderna i de minnesoptimerade tabellerna, trunkerar de diskbaserade tabellerna och kör en databaskontrollpunkt.
Följande skript körs samtidigt för att simulera en arbetsbelastning för bearbetning av försäljningsorder:
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
Med det här skriptet infogas varje provtagningsordning som är konstruerad 20 gånger, genom 20 lagrade procedurer som körs i en WHILE-loop. Loopen används för att ta hänsyn till det faktum att databasen används för att konstruera exempelordningen. I typiska produktionsmiljöer skapar mellannivåprogrammet den försäljningsorder som ska infogas.
Föregående skript infogar försäljningsorder i minnesoptimerade tabeller. Skriptet för att infoga försäljningsorder i diskbaserade tabeller härleds genom att ersätta de två förekomsterna av _inmem med _ondisk.
Vi använder ostress-verktyget för att köra skripten med flera samtidiga anslutningar. Vi använder parametern -n för att styra antalet anslutningar och parametern r för att styra hur många gånger skriptet körs på varje anslutning.
Kör arbetsuppgiften
För att testa i stor skala infogar vi 10 miljoner försäljningsorder med hjälp av 100 anslutningar. Det här testet fungerar rimligt på en blygsam server (till exempel 8 fysiska, 16 logiska kärnor) och grundläggande SSD-lagring för loggen. Om testet inte fungerar bra på maskinvaran kan du ta en titt på avsnittet Felsöka tester som körs långsamt. Om du vill minska stressnivån för det här testet sänker du antalet anslutningar genom att ändra parametern -n. Om du till exempel vill sänka antalet anslutningar till 40 ändrar du parametern -n100 till -n40.
Som ett prestandamått för arbetsbelastningen använder vi den förflutna tiden som rapporterats av ostress.exe när arbetsbelastningen har körts.
Följande instruktioner och mätningar använder en arbetsbelastning som infogar 10 miljoner försäljningsorder. Instruktioner för att köra en nedskalad arbetsbelastning som infogar 1 miljon försäljningsorder finns i instruktionerna som ingår i arkivet In-Memory OLTP\readme.txtSQLServer2016Samples.zip.
Minnesoptimerade tabeller
Vi börjar med att köra arbetsbelastningen på minnesoptimerade tabeller. Följande kommando öppnar 100 trådar, var och en körs för 5 000 iterationer. Varje iteration infogar 20 försäljningsorder i separata transaktioner. Det finns 20 infogningar per iteration för att kompensera för det faktum att databasen används för att generera de data som ska infogas. Detta ger totalt 20 * 5 000 * 100 = 10 000 000 försäljningsorderinfogningar.
Öppna RML-cmd prompten och kör följande kommando:
Välj knappen Kopiera för att kopiera kommandot och klistra in det i kommandotolken för RML Utilities.
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"
På en testserver med totalt 8 fysiska (16 logiska) kärnor tog det 2 minuter och 5 sekunder. På en andra testserver med 24 fysiska (48 logiska) kärnor tog det 1 minut och 0 sekunder.
Observera CPU-användningen medan arbetsbelastningen körs, till exempel med hjälp av Aktivitetshanteraren. Du ser att processoranvändningen är nära 100%. Om så inte är fallet har du en loggdatainmatningsflaskhals, se också Felsöka långsamma tester.
Diskbaserade tabeller
Följande kommando kör arbetsbelastningen på diskbaserade tabeller. Den här arbetsbelastningen kan ta ett tag att köra, vilket till stor del beror på spärrkonkurration i systemet. Minnesoptimerade tabeller är spärrfria och lider därför inte av det här problemet.
Öppna RML-cmd prompten och kör följande kommando:
Välj knappen Kopiera för att kopiera kommandot och klistra in det i kommandotolken för RML Utilities.
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"
På en testserver med totalt 8 fysiska (16 logiska) kärnor tog det 41 minuter och 25 sekunder. På en andra testserver med 24 fysiska (48 logiska) kärnor tog det 52 minuter och 16 sekunder.
Den viktigaste faktorn i prestandaskillnaden mellan minnesoptimerade tabeller och diskbaserade tabeller i det här testet är att SQL Server inte fullt ut kan använda processorn när du använder diskbaserade tabeller. Orsaken är spärrar: samtidiga transaktioner försöker skriva till samma datasida, och spärrar används för att säkerställa att endast en transaktion i taget kan skriva till en sida. Den In-Memory OLTP-motorn är spärrfri och datarader är inte ordnade på sidor. Samtidiga transaktioner blockerar alltså inte varandras infogningar, vilket gör att SQL Server kan utnyttja processorn fullt ut.
Du kan se processoranvändningen medan arbetsbelastningen körs, till exempel med hjälp av Aktivitetshanteraren. Du ser med diskbaserade tabeller att processoranvändningen är långt ifrån 100%. Vid en testkonfiguration med 16 logiska processorer ligger användningen på cirka 24%.
Du kan, om så önskas, visa antalet spärrväntningar per sekund med hjälp av Performance Monitor, med prestandaräknaren \SQL Server:Latches\Latch Waits/sec.
Återställ demonstrationen
Om du vill återställa demonstrationen öppnar du RML Cmd Prompt och kör följande kommando:
ostress.exe -S. -E -dAdventureWorks2022 -Q"EXEC Demo.usp_DemoReset"
Beroende på maskinvaran kan det ta några minuter att köra den.
Vi rekommenderar en återställning efter varje demokörning. Eftersom den här arbetsbelastningen endast är infogad förbrukar varje körning mer minne och därför krävs en återställning för att förhindra att minnet tar slut. Mängden minne som förbrukas efter en körning beskrivs i avsnittet Minnesanvändning när arbetsbelastningenhar körts.
Felsöka långsamt körande tester
Testresultaten varierar vanligtvis med maskinvara och även den samtidighetsnivå som används i testkörningen. Ett par saker att leta efter om resultatet inte är som förväntat:
- Antal samtidiga transaktioner: När du kör arbetsbelastningen på en enda tråd är prestandavinsten med In-Memory OLTP sannolikt mindre än 2X. Spärrkonkurrens är bara ett betydande problem om det finns hög grad av samtidighet. 
- Lågt antal kärnor som är tillgängliga för SQL Server: Det innebär att det finns en låg samtidighetsnivå i systemet, eftersom det bara kan finnas så många transaktioner som körs samtidigt som det finns kärnor tillgängliga för SQL. - Symptom: Om processoranvändningen är hög när du kör arbetsbelastningen på diskbaserade tabeller innebär det att det inte finns mycket tävlan, vilket pekar på brist på samtidighet.
 
- Loggenhetens hastighet: Om loggenheten inte kan hålla jämna steg med transaktionsgenomströmningen i systemet, blir arbetsbelastningen en flaskhals vid logg-I/O-operationer. Loggning är effektivare med In-Memory OLTP, men om logg-I/O är en flaskhals är den potentiella prestandavinsten begränsad. - Symptom: Om processoranvändningen inte är nära 100 % eller är väldigt varierande när du kör arbetsbelastningen på minnesoptimerade tabeller, kan det finnas en flaskhals i logg-I/O. Detta kan bekräftas genom att öppna Resursövervakaren och titta på kölängden för loggdisken.
 
Minnes- och diskutrymmesanvändning i exemplet
I följande exempel beskriver vi vad du kan förvänta dig när det gäller minnes- och diskutrymmesanvändning för exempeldatabasen. Vi visar också resultaten från på en testserver med 16 logiska kärnor.
Minnesanvändning för de minnesoptimerade tabellerna
Övergripande användning av databasen
Följande fråga kan användas för att hämta den totala minnesanvändningen för In-Memory OLTP i systemet.
SELECT type,
       name,
       pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Ögonblicksbild efter att databasen just har skapats:
| typ | Namn | sidor_MB | 
|---|---|---|
| MEMORYCLERK_XTP | Förvalt | 94 | 
| MEMORYCLERK_XTP | DB_ID_5 | 877 | 
| MEMORYCLERK_XTP | Förvalt | 0 | 
| MEMORYCLERK_XTP | Förvalt | 0 | 
Standardminneshanterarna innehåller systemomfattande minnesstrukturer och är relativt små. Minnestjänstemannen för användardatabasen, i det här fallet databasen med ID 5 ( database_id kan skilja sig åt i din instans), är cirka 900 MB.
Minnesanvändning per tabell
Följande fråga kan användas för att öka detaljnivån i minnesanvändningen för de enskilda tabellerna och deras 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';
I följande tabell visas resultatet av den här frågan för en ny installation av exemplet:
| Tabellnamn | 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 | 
Som du ser är tabellerna ganska små: SalesOrderHeader_inmem är cirka 7 MB och SalesOrderDetail_inmem är ungefär 15 MB stora.
Det som är slående här är storleken på det minne som allokerats för index, jämfört med storleken på tabelldata. Det beror på att hash-indexen i exemplet är fördefinierade för en större datastorlek. Hash-index har en fast storlek och därför växer inte deras storlek med storleken på data i tabellen.
Minnesanvändning när arbetsbelastningen har körts
När du har infogat 10 miljoner försäljningsorder ser all-up-minnesanvändningen ut ungefär som i följande fråga:
SELECT type,
       name,
       pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Här är resultatet.
| type | name | pages_MB | 
|---|---|---|
| MEMORYCLERK_XTP | Förvalt | 146 | 
| MEMORYCLERK_XTP | DB_ID_5 | 7374 | 
| MEMORYCLERK_XTP | Förvalt | 0 | 
| MEMORYCLERK_XTP | Förvalt | 0 | 
Som du ser använder SQL Server lite under 8 GB för de minnesoptimerade tabellerna och indexen i exempeldatabasen.
Titta på den detaljerade minnesanvändningen per tabell efter en exempelkörning:
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';
Här är resultatet.
| Table name | memory_allocated_for_table_kb | memory_allocated_for_indexes_kb | 
|---|---|---|
| SalesOrderDetail_inmem | 5113761 | 663552 | 
| Demoförsäljningsorderdetaljjämförelse | 64 | 10368 | 
| SpecialOffer_inmem | 2 | 8192 | 
| SalesOrderHeader_inmem | 1575679 | 147456 | 
| Product_inmem | 111 | 12032 | 
| SpecialOfferProduct_inmem | 64 | 3712 | 
| DemoFörsäljningsOrderHuvudFrö | 1984 | 5504 | 
Vi kan se totalt cirka 6,5 GB data. Storleken på indexen i tabellen SalesOrderHeader_inmem och SalesOrderDetail_inmem är samma som storleken på indexen innan du infogar försäljningsorder. Indexstorleken ändrades inte eftersom båda tabellerna använder hash-index och hash-index är statiska.
Efter demoåterställning
Den lagrade proceduren Demo.usp_DemoReset kan användas för att återställa demonstrationen. Den tar bort data i tabellerna SalesOrderHeader_inmem och SalesOrderDetail_inmem, och återställer data från de ursprungliga tabellerna SalesOrderHeader och SalesOrderDetail.
Även om raderna i tabellerna har tagits bort betyder det inte att minnet frigörs omedelbart. SQL Server återtar minne från borttagna rader i minnesoptimerade tabeller i bakgrunden efter behov. Du ser att direkt efter demoåterställning, utan transaktionsbelastning på systemet, har minnet från borttagna rader ännu inte återkrävts.
SELECT type,
       name,
       pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Här är resultatet.
| type | name | pages_MB | 
|---|---|---|
| MEMORYCLERK_XTP | Förvalt | 2261 | 
| MEMORYCLERK_XTP | DB_ID_5 | 7396 | 
| MEMORYCLERK_XTP | Förvalt | 0 | 
| MEMORYCLERK_XTP | Förvalt | 0 | 
Detta förväntas: minnet frigörs när transaktionsarbetsbelastningen körs.
Om du startar en andra körning av demoarbetsbelastningen ser du att minnesanvändningen minskade från början, eftersom de tidigare borttagna raderna rensas. Vid något tillfälle ökar minnesstorleken igen tills arbetsbelastningen är klar. När du har infogat 10 miljoner rader efter demoåterställningen är minnesanvändningen mycket lik användningen efter den första körningen. Till exempel:
SELECT type,
       name,
       pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Här är resultatet.
| type | name | pages_MB | 
|---|---|---|
| MEMORYCLERK_XTP | Förvalt | 1863 | 
| MEMORYCLERK_XTP | DB_ID_5 | 7390 | 
| MEMORYCLERK_XTP | Förvalt | 0 | 
| MEMORYCLERK_XTP | Förvalt | 0 | 
Diskanvändning för minnesoptimerade tabeller
Den övergripande storleken på disken för kontrollpunktsfilerna för en databas vid en viss tidpunkt finns med hjälp av frågan:
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';
Initialt tillstånd
När exempelfilgruppen och minnesoptimerade exempeltabeller skapas initialt skapas flera kontrollpunktsfiler och systemet börjar fylla filerna – antalet förskapade kontrollpunktsfiler beror på antalet logiska processorer i systemet. Eftersom exemplet inledningsvis är mycket litet är de förskapade filerna mestadels tomma efter den första skapande.
Följande kod visar den ursprungliga storleken på disken för exemplet på en dator med 16 logiska processorer:
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';
Här är resultatet.
| Storlek på disk i MB | 
|---|
| 2312 | 
Som du ser finns det en stor skillnad mellan den lagrade storleken på disken för kontrollfilerna, som är 2,3 GB, och den faktiska datastorleken, som är närmare 30 MB.
Om du tittar närmare på var diskutrymmesanvändningen kommer ifrån kan du använda följande fråga. Storleken på disken som returneras av den här frågan är ungefärlig för filer med tillstånd i 5 (KRÄVS FÖR SÄKERHETSKOPIERing/HA), 6 (I ÖVERGÅNG TILL TOMBSTONE) eller 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;
För exemplets ursprungliga tillstånd ser resultatet ut ungefär som i följande tabell för en server med 16 logiska processorer:
| tillstånd_beskrivning | file_type_desc | räkna | Storlek på disken MB | 
|---|---|---|---|
| FÖRSKAPAD | DATA | 16 | 2048 | 
| FÖRSKAPAD | DELTA | 16 | 128 | 
| Under konstruktion | DATA | 1 | 128 | 
| Under konstruktion | DELTA | 1 | 8 | 
Som du ser används det mesta av utrymmet av förskapade data och deltafiler. SQL Server förskapade ett par (data, delta) filer per logisk processor. Dessutom är datafilerna förstorlekssatta till 128 MB och deltafilerna till 8 MB för att göra infogning av data i dessa filer effektivare.
Faktiska data i de minnesoptimerade tabellerna finns i den enskilda datafilen.
När du har kört arbetslasten
Efter en enda testkörning som infogar 10 miljoner försäljningsorder ser den totala storleken på disken ut ungefär så här (för en 16-kärns testserver):
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';
Här är resultatet.
| Storlek på disk i MB | 
|---|
| 8828 | 
Storleken på disken är nära 9 GB, vilket är nära minnesintern storlek på data.
En närmare titt på storlekarna på kontrollpunktsfilerna i de olika tillstånden:
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;
Här är resultatet.
| state_desc | file_type_desc | count | on-disk size MB | 
|---|---|---|---|
| FÖRSKAPAD | DATA | 16 | 2048 | 
| FÖRSKAPAD | DELTA | 16 | 128 | 
| Under konstruktion | DATA | 1 | 128 | 
| Under konstruktion | DELTA | 1 | 8 | 
Vi har fortfarande 16 par förskapade filer, klara att användas när kontrollpunkter är stängda.
Det finns ett par under konstruktion, som används tills den nuvarande kontrollpunkten är stängd. Tillsammans med de aktiva kontrollpunktsfilerna ger detta cirka 6,5 GB diskanvändning för 6,5 GB data i minnet. Kom ihåg att index inte sparas på disken, och därför är den totala storleken på disken mindre än storleken i minnet i det här fallet.
Efter demoåterställning
Efter demoåterställningen frigörs inte diskutrymmet omedelbart om det inte finns någon transaktionsarbetsbelastning i systemet och det inte finns några kontrollpunkter för databasen. För att kontrollpunktsfiler ska kunna flyttas genom sina olika faser och så småningom kasseras, måste flera kontrollpunkter och loggtrunkeringsevent inträffa för att initiera sammanslagning av kontrollpunktsfiler samt initiering av skräpinsamling. Dessa sker automatiskt om du har en transaktionsarbetsbelastning i systemet (och utför regelbundna loggsäkerhetskopior, om du använder fullständig återställningsmodell), men inte när systemet är inaktivt, som i ett demoscenario.
I exemplet, efter demoåterställning, kan du se något i stil med:
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';
Här är resultatet.
| Storlek på disk i MB | 
|---|
| 11839 | 
Vid nästan 12 GB är detta betydligt mer än de 9 GB vi hade före demoåterställningen. Det beror på att vissa sammanslagningar av kontrollpunktsfiler har startats, men några av sammanslagningsmålen ännu inte har installerats och att vissa av källfilerna för sammanslagning ännu inte har rensats, vilket kan ses i följande exempel:
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;
Här är resultatet.
| state_desc | file_type_desc | count | on-disk size MB | 
|---|---|---|---|
| FÖRSKAPAD | DATA | 16 | 2048 | 
| FÖRSKAPAD | DELTA | 16 | 128 | 
| AKTIV | DATA | 38 | 5152 | 
| AKTIV | DELTA | 38 | 1331 | 
| SAMMANSLAGNINGSMÅL | DATA | 7 | 896 | 
| SAMMANSLAGNINGSMÅL | DELTA | 7 | 56 | 
| SAMMANFOGAD KÄLLA | DATA | 13 | 1772 | 
| SAMMANFOGAD KÄLLA | DELTA | 13 | 455 | 
Mål för sammanslagning installeras och den sammanfogade källan rensas när transaktionsaktiviteter sker i systemet.
Efter den andra körningen av demobelastningen, där 10 miljoner försäljningsorder infogades efter demoåterställningen, ser du att filerna som skapades under den första körningen rensades. Om du kör föregående fråga flera gånger medan arbetsbelastningen körs kan du se hur kontrollpunktsfilerna tar sig igenom de olika stegen.
Efter den andra körningen av arbetsbelastningen infogas 10 miljoner försäljningsorder, och du ser diskutnyttjande som är mycket likt, men inte nödvändigtvis samma som efter den första körningen, eftersom systemet är dynamiskt av naturen. Till exempel:
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;
Här är resultatet.
| state_desc | file_type_desc | count | on-disk size MB | 
|---|---|---|---|
| FÖRSKAPAD | DATA | 16 | 2048 | 
| FÖRSKAPAD | DELTA | 16 | 128 | 
| Under konstruktion | DATA | 2 | 268 | 
| Under konstruktion | DELTA | 2 | 16 | 
| AKTIV | DATA | 41 | 5608 | 
| AKTIV | DELTA | 41 | 328 | 
I det här fallet finns det två kontrollpunktsfilpar i UNDER CONSTRUCTION tillståndet, vilket innebär att flera filpar har flyttats till UNDER CONSTRUCTION tillståndet, sannolikt på grund av den höga samtidigheten i arbetsbelastningen. Flera samtidiga trådar krävde ett nytt filpar samtidigt och flyttade därför ett par från PRECREATED till UNDER CONSTRUCTION.