Condividi tramite


Database di esempio per OLTP in memoria

Si applica a:SQL Serverdatabase SQL di Azure

Panoramica

In questo esempio viene mostrata la funzionalità OLTP in memoria. Mostra tabelle ottimizzate per la memoria e stored procedure compilate in modo nativo e può essere usata per illustrare i vantaggi delle prestazioni di OLTP in memoria.

Nota

Per visualizzare questo articolo per SQL Server 2014 (12.x), vedere Estensioni di AdventureWorks per dimostrare In-Memory OLTP.

Nell'esempio viene eseguita la migrazione di cinque tabelle del database AdventureWorks2025 a tabelle ottimizzate per la memoria. Inoltre, è incluso un carico di lavoro dimostrativo di ordini di vendita. È possibile usare questo carico di lavoro demo per visualizzare il vantaggio delle prestazioni dell'uso di OLTP in memoria nel server.

Nella descrizione dell'esempio vengono illustrati i compromessi apportati durante la migrazione delle tabelle a OLTP in memoria per tenere conto delle funzionalità non ancora supportate per le tabelle ottimizzate per la memoria.

La documentazione di questo esempio è strutturata come segue:

Prerequisiti

  • SQL Server 2016 (13.x)

  • Per il test delle prestazioni, un server con specifiche simili all'ambiente di produzione. Per questo particolare esempio sono necessari almeno 16 GB di memoria disponibili per SQL Server. Per linee guida generali sull'hardware per OLTP in memoria, vedere il post di blog seguente: Considerazioni sull'hardware per In-Memory OLTP in SQL Server

Installare l'esempio OLTP in memoria basato su AdventureWorks

Per installare l'esempio, seguire i passaggi riportati di seguito.

  1. Scarica AdventureWorks2016_EXT.bak e SQLServer2016Samples.zip da: https://github.com/microsoft/sql-server-samples/releases/tag/adventureworks in una cartella locale, ad esempio C:\Temp.

  2. Ripristina il backup del database usando Transact-SQL o SQL Server Management Studio:

    1. Identificare la cartella di destinazione e il nome file per il file di dati, ad esempio:

      H:\DATA\AdventureWorks2022_Data.mdf
      
    2. Identificare la cartella di destinazione e il nome file per il file di log, ad esempio:

      I:\DATA\AdventureWorks2022_log.ldf
      
      1. Il file di log deve essere posizionato in un'unità diversa dal file di dati, idealmente un'unità a bassa latenza, ad esempio l'archiviazione sull'unità SSD o PCIe, per prestazioni ottimali.

    Script T-SQL di esempio:

    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. Per visualizzare gli script di esempio e il carico di lavoro, decomprimere il file SQLServer2016Samples.zip in una cartella locale. Consultare il file In-Memory OLTP\readme.txt per istruzioni sull'esecuzione del carico di lavoro.

Descrizione delle tabelle e delle procedure di esempio

Nell'esempio vengono create nuove tabelle per i prodotti e gli ordini vendita, basate sulle tabelle esistenti in AdventureWorks2025. Lo schema delle nuove tabelle è simile alle tabelle esistenti, con alcune differenze, come illustrato più avanti in questa sezione.

Le nuove tabelle ottimizzate per la memoria contengono il suffisso _inmem. L'esempio include anche le tabelle corrispondenti che contengono il suffisso _ondisk . Queste tabelle possono essere usate per eseguire un confronto uno-a-uno tra le prestazioni delle tabelle ottimizzate per la memoria e le tabelle basate su disco nel sistema.

Le tabelle ottimizzate per la memoria usate nel carico di lavoro per il confronto delle prestazioni sono completamente durevoli e con registrazione completa. Non sacrificano la durabilità o l'affidabilità per ottenere il miglioramento delle prestazioni.

Il carico di lavoro di destinazione per questo esempio è rappresentato dall'elaborazione degli ordini vendita, in cui vengono prese in considerazione anche le informazioni sui prodotti e sugli sconti. A questo scopo, si usano le tabelle SalesOrderHeader, SalesOrderDetail, Product, SpecialOffer e SpecialOfferProduct.

Per inserire gli ordini vendita e aggiornare le informazioni sulla spedizione di un determinato ordine vendita vengono utilizzate due nuove stored procedure: Sales.usp_InsertSalesOrder_inmem e Sales.usp_UpdateSalesOrderShipInfo_inmem.

Nel nuovo schema Demo sono incluse tabelle di supporto e stored procedure per eseguire un carico di lavoro dimostrativo.

In concreto, nell'esempio di OLTP in memoria vengono aggiunti ad AdventureWorks2025 gli oggetti seguenti:

Tabelle aggiunte dal campione

Nuove tabelle

Sales.SalesOrderHeader_inmem

  • Informazioni di intestazione sugli ordini di vendita. Ogni ordine vendita è rappresentato da una riga nella tabella.

Sales.SalesOrderDetail_inmem

  • Dettagli degli ordini vendita. Ogni voce di un ordine vendita è rappresentato da una riga nella tabella.

Sales.SpecialOffer_inmem

  • Informazioni sulle offerte speciali, inclusa la percentuale di sconto associata a ogni offerta.

Sales.SpecialOfferProduct_inmem

  • Tabella di riferimento tra offerte speciali e prodotti. Ogni offerta speciale può includere zero o più prodotti e ogni prodotto può essere caratterizzato da zero o più offerte speciali.

Production.Product_inmem

  • Informazioni sui prodotti, tra cui il prezzo di listino.

Demo.DemoSalesOrderDetailSeed

  • Utilizzata nel carico di lavoro dimostrativo per creare ordini di vendita di esempio.

Varianti basate su disco delle tabelle:

  • Sales.SalesOrderHeader_ondisk

  • Sales.SalesOrderDetail_ondisk

  • Sales.SpecialOffer_ondisk

  • Sales.SpecialOfferProduct_ondisk

  • Production.Product_ondisk

Differenze tra le tabelle basate su disco originali e le nuove tabelle ottimizzate per la memoria

In genere, le nuove tabelle introdotte da questo esempio usano le stesse colonne e gli stessi tipi di dati delle tabelle originali. Tuttavia, esistono alcune differenze Vengono elencate le differenze in questa sezione, insieme a una logica per le modifiche.

Sales.SalesOrderHeader_inmem

  • Vincoli predefiniti. Sono supportati per le tabelle ottimizzate per la memoria; inoltre, la migrazione della maggior parte di questi vincoli è stata eseguita senza apportarvi variazioni. Tuttavia, nella tabella originale Sales.SalesOrderHeader sono contenuti due vincoli predefiniti tramite cui viene recuperata la data corrente per le colonne OrderDate e ModifiedDate. In un carico di lavoro di elaborazione ordini ad alta velocità di trasmissione con molta competizione simultanea, qualsiasi risorsa globale può diventare un punto di conflitto. Il tempo di sistema è una risorsa così globale e abbiamo osservato che può diventare un collo di bottiglia durante l'esecuzione di un carico di lavoro OLTP In-Memory che inserisce ordini di vendita, particolarmente se è necessario recuperare il tempo di sistema per più colonne nell'intestazione dell'ordine di vendita e nei dettagli dell'ordine di vendita. In questo esempio il problema è stato risolto recuperando l'ora di sistema solo una volta per ogni ordine vendita inserito e il valore in questione viene utilizzato per le colonne di tipo datetime in SalesOrderHeader_inmem e in SalesOrderDetail_inmem, nella stored procedure Sales.usp_InsertSalesOrder_inmem.

  • Tipi di dati definiti dall'utente alias (UDT) - Nella tabella originale vengono utilizzati due alias UDT, dbo.OrderNumber e dbo.AccountNumber, rispettivamente per le colonne PurchaseOrderNumber e AccountNumber. SQL Server 2016 (13.x) non supporta l'alias UDT per le tabelle ottimizzate per la memoria, pertanto le nuove tabelle usano rispettivamente i tipi di dati di sistema nvarchar(25) e nvarchar(15).

  • Colonne annullabili nelle chiavi di indice: nella tabella originale la colonna SalesPersonID è annullabile, mentre nelle nuove tabelle la colonna non è annullabile e ha un vincolo predefinito con valore (-1). Questa circostanza è dovuta al fatto che gli indici nelle tabelle ottimizzate per la memoria non possono avere colonne nullable nella chiave di indice; -1 è un surrogato per NULL in questo caso.

  • Colonne calcolate : le colonne SalesOrderNumber calcolate e TotalDue vengono omesse, in quanto SQL Server 2016 (13.x) non supporta le colonne calcolate nelle tabelle ottimizzate per la memoria. La nuova vista Sales.vSalesOrderHeader_extended_inmem riflette le colonne SalesOrderNumber e TotalDue. Pertanto, puoi utilizzare questa vista se queste colonne sono necessarie.

    • Si applica a: SQL Server 2017 (14.x). A partire da SQL Server 2017 (14.x), le colonne calcolate sono supportate nelle tabelle e negli indici ottimizzati per la memoria.
  • In SQL Server 2016 (13.x) sono supportati i vincoli di chiave esterna per le tabelle ottimizzate per la memoria, ma solo se anche le tabelle di riferimento sono ottimizzate per la memoria. Le chiavi esterne che fanno riferimento a tabelle che sono anch'esse migrate per essere ottimizzate per la memoria sono mantenute nelle tabelle migrate, mentre le altre chiavi esterne vengono omesse. Inoltre, SalesOrderHeader_inmem è una tabella ad accesso frequente nel carico di lavoro di esempio e i vincoli di chiavi esterne richiedono un'elaborazione aggiuntiva per tutte le operazioni DML, perché richiede ricerche in tutte le altre tabelle a cui si fa riferimento in questi vincoli. Di conseguenza, il presupposto è che l'app garantisce l'integrità referenziale per la Sales.SalesOrderHeader_inmem tabella e l'integrità referenziale non viene convalidata quando vengono inserite righe.

  • Rowguid . La colonna rowguid viene omessa. Sebbene uniqueidentifier sia supportato per le tabelle ottimizzate per la memoria, l'opzione ROWGUIDCOL non è supportata in SQL Server 2016 (13.x). Le colonne di questo tipo sono generalmente utilizzate per la replica merge o per le tabelle con colonne FILESTREAM. Nell'esempio sono escluse entrambe.

Vendite.DettaglioOrdineDiVendita

  • Vincoli predefiniti : analogamente a SalesOrderHeader, il vincolo predefinito che richiede la data/ora di sistema non viene migrato. Invece, la procedura memorizzata che inserisce gli ordini di vendita inserisce la data/ora di sistema corrente al primo inserimento.

  • Colonne calcolate : la colonna LineTotal calcolata non è stata migrata perché le colonne calcolate non sono supportate con tabelle ottimizzate per la memoria in SQL Server 2016 (13.x). Per accedere a questa colonna, usare la Sales.vSalesOrderDetail_extended_inmem vista .

  • Rowguid. La colonna rowguid viene omessa. Per informazioni dettagliate, vedi la descrizione della tabella SalesOrderHeader.

Produzione.Prodotto

  • Tipi alias definiti dall'utente (UDT): nella tabella originale viene usato il tipo di dati definito dall'utente dbo.Flag, equivalente al bit del tipo di dati di sistema. Nella tabella migrata viene utilizzato, in alternativa, il tipo di dati bit.

  • Rowguid. La colonna rowguid viene omessa. Per informazioni dettagliate, vedi la descrizione della tabella SalesOrderHeader.

Vendite.OffertaSpeciale

  • Rowguid. La colonna rowguid viene omessa. Per informazioni dettagliate, vedi la descrizione della tabella SalesOrderHeader.

Vendite.ProdottoOffertaSpeciale

  • Rowguid. La colonna rowguid viene omessa. Per informazioni dettagliate, vedi la descrizione della tabella SalesOrderHeader.

Considerazioni sugli indici nelle tabelle ottimizzate per la memoria

L'indice di base per le tabelle ottimizzate per la memoria è l'indice NONCLUSTERED, che supporta le ricerche di punti (ricerca nell'indice nel predicato di uguaglianza), le analisi dell'intervallo (ricerca nell'indice nel predicato di disuguaglianza), analisi di indici completi e analisi ordinate. Inoltre, gli indici NONCLUSTERED supportano la ricerca nelle colonne iniziali della chiave di indice. In realtà, gli indici NONCLUSTERED ottimizzati per la memoria supportano tutte le operazioni consentite dagli indici NONCLUSTERED basati su disco, con la sola eccezione che vengono eseguite analisi a ritroso. Pertanto, l'utilizzo di indici NONCLUSTERED è una scelta sicura.

Per ottimizzare ulteriormente il carico di lavoro, è possibile usare gli indici HASH. Vengono ottimizzati per le ricerche di punti e per gli inserimenti di righe. Tuttavia, è necessario considerare che non supportano analisi di intervalli, analisi ordinate o ricerche sulle colonne chiave dell'indice iniziali. Di conseguenza, prestare attenzione quando si utilizzano questi indici. Inoltre, è necessario specificare bucket_count al momento della creazione. In genere deve essere impostato su un valore compreso tra una e due volte il numero di valori di chiave di indice; tuttavia, un valore superiore non rappresenta di solito un problema.

Per altre informazioni:

Gli indici nelle tabelle di cui è stata eseguita la migrazione sono stati ottimizzati per il carico di lavoro di elaborazione degli ordini di vendita demo. Il carico di lavoro si basa su inserimenti e ricerche di punti nelle tabelle Sales.SalesOrderHeader_inmem e Sales.SalesOrderDetail_inmem e si basa anche sulle ricerche di punti sulle colonne chiave primaria nelle tabelle Production.Product_inmem e Sales.SpecialOffer_inmem.

Sales.SalesOrderHeader_inmem prevede tre indici, che sono tutti di tipo HASH per motivi di prestazioni e perché per il carico di lavoro non sono necessarie analisi ordinate, né dell'intervallo.

  • Indice HASH in (SalesOrderID): il bucket_count viene ridimensionato a 10 milioni (arrotondato a 16 milioni), poiché il numero previsto di ordini di vendita è pari a 10 milioni.

  • Indice HASH in (SalesPersonID): il bucket_count è pari a 1 milione. Il set di dati fornito non ha molte persone di vendita. Però questa ampia 'bucket_count' permette un'espansione futura. Inoltre, non si subisce una penalizzazione delle prestazioni per le ricerche se il bucket_count è sovradimensionato.

  • Indice HASH in (CustomerID): il bucket_count è pari a 1 milione. Il set di dati fornito non ha molti clienti, ma ciò consente una crescita futura.

Sales.SalesOrderDetail_inmem prevede tre indici, che sono tutti di tipo HASH per motivi di prestazioni e perché per il carico di lavoro non sono necessarie analisi ordinate, né dell'intervallo.

  • Indice HASH in (SalesOrderID, SalesOrderDetailID): si tratta dell'indice di chiave primaria e, anche se le ricerche su (SalesOrderID, SalesOrderDetailID) sono poco frequenti, l'uso di un indice hash per la chiave accelera gli inserimenti di righe. Il bucket_count viene ridimensionato a 50 milioni (arrotondato fino a 67 milioni); il numero previsto di ordini vendita è 10 milioni che viene ridimensionato per ottenere una media di cinque articoli per ordine.

  • Indice HASH su (SalesOrderID): le ricerche in base all'ordine di vendita sono frequenti: si desidera trovare tutti gli elementi di riga corrispondenti a un singolo ordine. Il bucket_count viene ridimensionato a 10 milioni (arrotondato fino a 16 milione), poiché il numero previsto di ordini vendita è pari a 10 milioni.

  • Indice HASH in (ProductID): il bucket_count è pari a 1 milione. Il set di dati fornito non ha molti prodotti, ma ciò consente una crescita futura.

Production.Product_inmem prevede tre indici

  • Indice HASH in (ProductID): le ricerche in ProductID si trovano in un percorso critico del carico di lavoro dimostrativo, pertanto si tratta di un indice hash

  • Indice NONCLUSTERED su (Name): consente l'analisi ordinata dei nomi dei prodotti

  • Indice NONCLUSTERED su (ProductNumber): consente scansioni ordinate dei numeri di prodotto

Sales.SpecialOffer_inmem ha un indice HASH su (SpecialOfferID): le consultazioni puntuali delle offerte speciali si trovano nella parte critica del carico di lavoro della demo. Il bucket_count viene ridimensionato a 1 milione per tener conto della crescita futura.

Sales.SpecialOfferProduct_inmem non è menzionato nel carico di lavoro demo, e quindi non sembra necessario usare indici hash su questa tabella per ottimizzare il carico di lavoro; gli indici su (SpecialOfferID, ProductID) e (ProductID) sono NONCLUSTERED.

Nell'esempio precedente, alcuni conteggi dei bucket vengono sovradimensionati, ma non i conteggi dei bucket per gli indici su SalesOrderHeader_inmem e SalesOrderDetail_inmem: sono dimensionati per soli 10 milioni di ordini di vendita. Questa operazione è stata eseguita per consentire l'installazione dell'esempio nei sistemi con bassa disponibilità di memoria, anche se in questi casi il carico di lavoro dimostrativo ha esito negativo con un errore di memoria insufficiente. Se si desidera scalare ben oltre i 10 milioni di ordini di vendita, è possibile aumentare i conteggi dei bucket di conseguenza.

Considerazioni sull'utilizzo della memoria

L'utilizzo della memoria nel database di esempio, sia prima che dopo l'esecuzione del carico di lavoro dimostrativo, è descritto nella sezione Utilizzo della memoria per le tabelle ottimizzate per la memoria.

Le procedure memorizzate aggiunte dall'esempio

Di seguito sono riportate due procedure memorizzate principali per l'inserimento dell'ordine di vendita e l'aggiornamento dei dettagli di spedizione.

  • Sales.usp_InsertSalesOrder_inmem

    • Inserisce un nuovo ordine di vendita nel database e restituisce SalesOrderID per quell'ordine di vendita. Come parametri di input accetta i dettagli per l'intestazione dell'ordine di vendita e le voci dell'ordine.

    • Parametro di output:

      • @SalesOrderID int : per SalesOrderID l'ordine di vendita appena inserito
    • Parametri di input (obbligatori):

      • @DueDatedatetime2
      • @CustomerIDint
      • @BillToAddressIDint
      • @ShipToAddressIDint
      • @ShipMethodIDint
      • Sales.SalesOrderDetailType_inmem@SalesOrderDetails - parametro con valori di tabella (TVP) che contiene le voci dell'ordine
    • Parametri di input (facoltativi):

      • @Statustinyint
      • @OnlineOrderFlagbit
      • @PurchaseOrderNumbernvarchar(25)
      • @AccountNumbernvarchar(15)
      • @SalesPersonIDint
      • @TerritoryIDint
      • @CreditCardIDint
      • @CreditCardApprovalCodevarchar(15)
      • @CurrencyRateIDint
      • @Commentnvarchar(128)
  • Sales.usp_UpdateSalesOrderShipInfo_inmem

    • Aggiornare le informazioni sulla spedizione per un ordine vendita specificato. In questo modo vengono aggiornate anche le informazioni di spedizione per tutti gli articoli dell'ordine di vendita.

    • Si tratta di una procedura wrapper per le procedure compilate a livello nativo Sales.usp_UpdateSalesOrderShipInfo_native, dotata di logica di riesecuzione per gestire i potenziali conflitti imprevisti con transazioni simultanee che aggiornano lo stesso ordine. Per altre informazioni, vedi Logica di ripetizione dei tentativi.

  • Sales.usp_UpdateSalesOrderShipInfo_native

    • Si tratta della stored procedure compilata a livello nativo tramite cui viene elaborato effettivamente l'aggiornamento delle informazioni sulla spedizione. Deve essere chiamato dalla procedura memorizzata wrapper Sales.usp_UpdateSalesOrderShipInfo_inmem. Se il client è in grado di gestire gli errori e implementare la logica di riesecuzione, puoi chiamare direttamente questa procedura, anziché utilizzare la procedura memorizzata wrapper.

La seguente procedura memorizzata viene utilizzata per il carico di lavoro dimostrativo.

  • Demo.usp_DemoReset

    • Reimposta la demo svuotando e ripopolando le tabelle SalesOrderHeader e SalesOrderDetail.

Le procedure memorizzate seguenti sono utilizzate per l'inserimento e l'eliminazione nelle tabelle ottimizzate per la memoria, garantendo al contempo l'integrità di dominio e referenziale.

  • Production.usp_InsertProduct_inmem
  • Production.usp_DeleteProduct_inmem
  • Sales.usp_InsertSpecialOffer_inmem
  • Sales.usp_DeleteSpecialOffer_inmem
  • Sales.usp_InsertSpecialOfferProduct_inmem

Infine, la seguente procedura memorizzata viene utilizzata per verificare l'integrità di dominio e referenziale.

  1. dbo.usp_ValidateIntegrity

    • Parametro facoltativo: @object_id - ID dell'oggetto per convalidare l'integrità

    • Questa procedura si basa sulle tabelle dbo.DomainIntegrity, dbo.ReferentialIntegrity e dbo.UniqueIntegrity per le regole di integrità che devono essere verificate. Nell'esempio queste tabelle vengono popolate in base ai vincoli CHECK, di chiave esterna e univoci presenti per le tabelle originali nel database AdventureWorks2025.

    • Si basa sulle procedure helper dbo.usp_GenerateCKCheck, dbo.usp_GenerateFKCheck e dbo.GenerateUQCheck per generare il T-SQL necessario per eseguire i controlli di integrità.

Misurazioni delle prestazioni con il carico di lavoro demo

ostress è uno strumento da riga di comando sviluppato dal team di supporto di Microsoft CSS SQL Server. Questo strumento può essere utilizzato per eseguire query o procedure memorizzate in parallelo. È possibile configurare il numero di thread per eseguire una determinata istruzione T-SQL in parallelo ed è possibile specificare quante volte deve essere eseguita l'istruzione in questo thread; ostress ruota i thread ed esegue l'istruzione su tutti i thread in parallelo. Al termine dell'esecuzione per tutti i thread, ostress segnala il tempo impiegato per il completamento dell'esecuzione di tutti i thread.

Procedere all'installazione di ostress

ostress viene installato come parte delle utilità RML (Report Markup Language); non esiste un'installazione autonoma per ostress.

Passaggi dell'installazione:

  1. Scarica ed esegui il pacchetto di installazione x64 per le utilità RML dalla pagina seguente: Download Report Markup Language (RML) for SQL Server (Scaricare RML (Report Markup Language) per SQL Server)

  2. Se è presente una finestra di dialogo che indica che alcuni file sono in uso, selezionare "Continua"

Eseguire ostress

Ostress viene eseguito dal prompt della riga di comando. È più comodo eseguire lo strumento dal prompt dei comandi RML, installato come parte delle utilità RML.

Per aprire il prompt dei comandi RML, segui queste istruzioni:

In Windows apri il menu Start selezionando il tasto Windows e digita rml. Selezionare Richiesta cmd RML, che si trova nell'elenco dei risultati della ricerca.

Verificare che il prompt dei comandi si trovi nella cartella di installazione delle utilità RML.

Le opzioni della riga di comando per ostress possono essere visualizzate quando si esegue ostress.exe semplicemente senza opzioni della riga di comando. Le opzioni principali da considerare per l'esecuzione di ostress con questo esempio sono le seguenti:

Opzione Description
-S Nome dell'istanza di SQL Server a cui connettersi.
-E Usare l'autenticazione di Windows per connettersi (impostazione predefinita); se si usa l'autenticazione di SQL Server, usare le opzioni -U e -P specificare rispettivamente il nome utente e la password.
-d Nome del database, per questo esempio AdventureWorks2025.
-Q Istruzione T-SQL da eseguire.
-n Numero di connessioni che elaborano ogni file/query di input.
-r Numero di iterazioni per ogni connessione per l'esecuzione di ogni file/query di input.

Carico di lavoro demo

La procedura archiviata principale utilizzata per l'attività dimostrativa è Sales.usp_InsertSalesOrder_inmem/ondisk. Lo script nell'esempio seguente costruisce un parametro con valori di tabella (TVP) con dati di esempio e chiama la procedura per inserire un ordine di vendita con cinque voci.

Lo strumento ostress viene usato per eseguire le chiamate di stored procedure in parallelo, per simulare i client che inseriscono gli ordini di vendita contemporaneamente.

Reimposta la dimostrazione dopo ogni esecuzione di test di stress eseguendo Demo.usp_DemoReset. Tramite questa procedura vengono eliminate le righe nelle tabelle ottimizzate per la memoria, vengono troncate le tabelle basate su disco e viene eseguito un checkpoint del database.

Lo script seguente viene eseguito simultaneamente per simulare un carico di lavoro di elaborazione degli ordini vendita:

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

Con questo script, ogni ordine di esempio creato viene inserito 20 volte, utilizzando 20 stored procedure eseguite all'interno di un ciclo WHILE. Il ciclo viene utilizzato per tenere conto del fatto che il database viene utilizzato per costruire l'ordine di esempio. In ambienti di produzione tipici, l'applicazione di livello intermedio costruisce l'ordine di vendita da inserire.

Lo script precedente inserisce gli ordini di vendita nelle tabelle ottimizzate per la memoria. Lo script per inserire gli ordini di vendita nelle tabelle basate su disco è derivato sostituendo le due occorrenze di _inmem con _ondisk.

Lo strumento ostress viene usato per eseguire gli script usando diverse connessioni simultanee. Viene usato il parametro -n per controllare il numero di connessioni e il parametro r per controllare il numero di esecuzioni dello script in ogni connessione.

Eseguire il carico di lavoro

Per testare su larga scala, inseriamo 10 milioni di ordini di vendita, utilizzando 100 connessioni. Questo test viene eseguito ragionevolmente in un server modesto, ad esempio con 8 core fisici e 16 logici, e un'archiviazione sull'unità SSD di base per il log. Se il test non funziona correttamente sull'hardware, vedere la sezione Risolvere i problemi relativi ai test a esecuzione lenta. Per ridurre il livello di stress per questo test, ridurre il numero di connessioni modificando il parametro -n. Ad esempio, per abbassare il numero di connessioni a 40, modificare il parametro -n100 in -n40.

Come misura delle prestazioni per il carico di lavoro si usa il tempo trascorso come segnalato da ostress.exe dopo l'esecuzione del carico di lavoro.

Le seguenti istruzioni e misurazioni usano un carico di lavoro per l'inserimento di 10 milioni di ordini di acquisto. Per istruzioni sull'esecuzione di un carico di lavoro ridotto che inserisce 1 milione di ordini di vendita, vedere le contenute in In-Memory OLTP\readme.txt, che sono parte dell'archivio SQLServer2016Samples.zip.

Tabelle ottimizzate per la memoria

Per iniziare, eseguire il carico di lavoro nelle tabelle ottimizzate per la memoria. Tramite il comando seguente vengono aperti 100 thread, ognuno in esecuzione per 5.000 iterazioni. Tramite ogni iterazione vengono inseriti 20 ordini vendita in transazioni separate. Vi sono 20 inserimenti per ogni iterazione per compensare il fatto che il database viene utilizzato per generare i dati da inserire. Ciò produce un totale pari a 20 * 5.000 * 100 = 10.000.000 di inserimenti di ordini vendita.

Aprire il prompt dei comandi RML ed eseguire il comando seguente:

Seleziona il pulsante Copia per copiare il comando e incollarlo nel prompt dei comandi delle utilità 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"

In un server di test con un numero totale di 8 core fisici (16 logici), l'operazione ha richiesto 2 minuti e 5 secondi. In un secondo server di prova con 24 core fisici (48 logici), l'operazione ha richiesto 1 minuto e 0 secondi.

Osservare l'utilizzo della CPU mentre il carico di lavoro è in esecuzione, ad esempio tramite Gestione attività. Si noterà che l'utilizzo della CPU è vicino a 100%. Se questo non è il caso, è presente un collo di bottiglia I/O del registro. Consultare anche Risolvere i problemi relativi ai test a esecuzione lenta.

Tabelle basate su disco

Il comando seguente esegue il carico di lavoro nelle tabelle basate su disco. L'esecuzione di questo carico di lavoro potrebbe richiedere molto tempo, in gran parte dovuto alla contenzione di latch nel sistema. Le tabelle ottimizzate per la memoria sono senza latch e pertanto non soffrono di questo problema.

Aprire il prompt dei comandi RML ed eseguire il comando riportato di seguito:

Seleziona il pulsante Copia per copiare il comando e incollarlo nel prompt dei comandi delle utilità 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"

In un server di prova con un numero totale di 8 core fisici (16 logici), l'operazione ha richiesto 41 minuti e 25 secondi. In un secondo server di prova con 24 core fisici (48 logici), l'operazione ha richiesto 52 minuti e 16 secondi.

Il fattore principale nella differenza di prestazioni tra tabelle ottimizzate per la memoria e tabelle basate su disco in questo test è che quando si usano tabelle basate su disco, SQL Server non può usare completamente la CPU. Il motivo è la contesa di latch: tramite le transazioni simultanee si tenta di scrivere nella stessa pagina di dati; i latch vengono utilizzati per garantire che in una pagina venga scritta una sola transazione per volta. Il motore OLTP In-Memory è privo di latch e le righe di dati non sono organizzate in pagine. Pertanto, le transazioni simultanee non bloccano gli inserimenti degli altri, consentendo così a SQL Server di utilizzare completamente la CPU.

È possibile osservare l'utilizzo della CPU mentre il carico di lavoro è in esecuzione, ad esempio tramite Gestione attività. Si noterà che con le tabelle basate su disco l'utilizzo della CPU è lontano da 100%. In una configurazione di prova con 16 processori logici, l'utilizzo si aggira intorno al 24%.

Se lo desideri, è possibile visualizzare il numero di attese latch al secondo utilizzando Performance Monitor, con il contatore delle prestazioni \SQL Server:Latches\Latch Waits/sec.

Reimpostare la demo

Per reimpostare la dimostrazione, aprire il prompt dei comandi RML ed eseguire il comando riportato di seguito:

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

A seconda dell'hardware, l'esecuzione potrebbe richiedere alcuni minuti.

Si consiglia una reimpostazione dopo ogni esecuzione della dimostrazione. Poiché questo carico di lavoro è di sola inserimento, ogni esecuzione utilizza più memoria e pertanto è necessaria una reimpostazione per evitare l'esaurimento della memoria. La quantità di memoria utilizzata dopo un'esecuzione è descritta nella sezione Utilizzo della memoria dopo l'esecuzione del carico di lavoro.

Risolvere i problemi relativi ai test a esecuzione lenta

I risultati dei test variano in genere con l'hardware e anche il livello di concorrenza usato nell'esecuzione del test. Un paio di aspetti da cercare se i risultati non sono come previsto:

  • Numero di transazioni simultanee: quando si esegue il carico di lavoro in un singolo thread, è probabile che il miglioramento delle prestazioni con In-Memory OLTP sia inferiore a 2X. La contesa di latch è solo un problema significativo se esiste un livello elevato di concorrenza.

  • Numero ridotto di core disponibili per SQL Server: ciò significa che il sistema ha un basso livello di parallelismo, poiché il numero di transazioni che possono essere eseguite contemporaneamente è limitato al numero di core disponibili per SQL.

    • Sintomo: se l'utilizzo della CPU è elevato quando si esegue il carico di lavoro in tabelle basate su disco, ciò significa che non è presente un numero elevato di conflitti, a indicare una mancanza di concorrenza.
  • Velocità dell'unità di log: se l'unità di log non riesce a mantenere il livello di velocità effettiva delle transazioni nel sistema, il carico di lavoro diventa collo di bottiglia nell'I/O del log. Sebbene la registrazione sia più efficiente con OLTP in memoria, se le operazioni di I/O del log rappresentano un collo di bottiglia, il potenziale miglioramento delle prestazioni sarà limitato.

    • Sintomo: se l'utilizzo della CPU non è vicino al 100% o è molto irregolare quando si esegue il carico di lavoro nelle tabelle ottimizzate per la memoria, è possibile che si verifichi un collo di bottiglia dell'I/O del log. Questa situazione può essere verificata aprendo il monitoraggio risorse ed esaminando la lunghezza della coda per l'unità dei log.

Utilizzo di memoria e spazio su disco nell'esempio

Nell'esempio seguente viene descritto cosa aspettarsi in termini di utilizzo della memoria e dello spazio su disco per il database di esempio. Vengono inoltre visualizzati i risultati di in un server di test con 16 core logici.

Utilizzo della memoria per le tabelle ottimizzate per la memoria

Utilizzo complessivo del database

La query seguente può essere usata per ottenere l'utilizzo totale della memoria per OLTP in memoria nel sistema.

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

Snapshot dopo aver appena creato il database:

tipo nome pagine_MB
MEMORYCLERK_XTP Predefinito 94
MEMORYCLERK_XTP DB_ID_5 877
MEMORYCLERK_XTP Predefinito 0
MEMORYCLERK_XTP Predefinito 0

I gestori di memoria predefiniti contengono strutture di memoria di sistema e sono relativamente piccole. Il clerk di memoria per il database utente, in questo caso il database con ID 5 (potrebbe differire nell'istanza database_id ), è di circa 900 MB.

Utilizzo della memoria per ogni tabella

La query seguente può essere utilizzata per eseguire il drill-down nell'utilizzo della memoria delle singole tabelle e dei relativi indici:

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';

Nella tabella che segue sono visualizzati i risultati di questa query per un'installazione aggiornata dell'esempio:

Nome della tabella 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

Come si può notare, le tabelle sono abbastanza piccole: SalesOrderHeader_inmem è circa 7 MB e SalesOrderDetail_inmem ha dimensioni di circa 15 MB.

Ciò che colpisce qui sono le dimensioni della memoria allocata per gli indici, rispetto alle dimensioni dei dati della tabella. Ciò è dovuto al fatto che gli indici hash nell'esempio vengono preconfigurati per una dimensione di dati maggiore. Gli indici hash hanno una dimensione fissa e pertanto le dimensioni non aumentano con le dimensioni dei dati nella tabella.

Utilizzo della memoria dopo l'esecuzione del carico di lavoro

Dopo l'inserimento di 10 milioni di ordini di vendita, l'utilizzo complessivo della memoria appare simile alla query seguente:

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

Il set di risultati è il seguente.

type name pages_MB
MEMORYCLERK_XTP Predefinito 146
MEMORYCLERK_XTP DB_ID_5 7374
MEMORYCLERK_XTP Predefinito 0
MEMORYCLERK_XTP Predefinito 0

Come si può notare, SQL Server sta utilizzando un po' meno di 8 GB per le tabelle e gli indici ottimizzati per la memoria nel database di esempio.

Esaminare l'utilizzo dettagliato della memoria per tabella dopo un'esecuzione di esempio:

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';

Il set di risultati è il seguente.

Table name memory_allocated_for_table_kb memory_allocated_for_indexes_kb
SalesOrderDetail_inmem 5113761 663552
DemoDettaglioOrdineVenditeSeed 64 10368
OffertaSpeciale_inmem 2 8192
SalesOrderHeader_inmem 1575679 147456
Product_inmem 111 12032
ProdottoOffertaSpeciale_inmem 64 3712
DemoSalesOrderHeaderSeed 1984 5504

Si può notare un totale pari a circa 6,5 GB di dati. La dimensione degli indici nella tabella SalesOrderHeader_inmem e SalesOrderDetail_inmem corrisponde alla dimensione degli indici prima di inserire gli ordini di vendita. Le dimensioni dell'indice non sono state modificate perché entrambe le tabelle usano indici hash e gli indici hash sono statici.

Dopo il reset della demo

La stored procedure Demo.usp_DemoReset può essere utilizzata per reimpostare la dimostrazione. Elimina i dati nelle tabelle SalesOrderHeader_inmem e SalesOrderDetail_inmeme quindi li reinsedia dalle tabelle SalesOrderHeader originali e SalesOrderDetail.

Ora, anche se le righe nelle tabelle sono state eliminate, ciò non significa che la memoria venga recuperata immediatamente. SQL Server recupera memoria in background dalle righe eliminate nelle tabelle ottimizzate per la memoria, in base alle esigenze. Si noterà che immediatamente dopo la reimpostazione demo, senza alcun carico di lavoro transazionale nel sistema, la memoria delle righe eliminate non viene ancora recuperata:

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

Il set di risultati è il seguente.

type name pages_MB
MEMORYCLERK_XTP Predefinito 2261
MEMORYCLERK_XTP DB_ID_5 7396
MEMORYCLERK_XTP Predefinito 0
MEMORYCLERK_XTP Predefinito 0

Questo è previsto: la memoria viene recuperata quando il carico di lavoro transazionale è in esecuzione.

Se si avvia una seconda esecuzione del carico di lavoro demo, inizialmente si noterà che l'utilizzo della memoria diminuisce, perché le righe eliminate in precedenza vengono pulite. A un certo punto, le dimensioni della memoria aumentano di nuovo fino al termine del carico di lavoro. Dopo aver inserito 10 milioni di righe dopo la reimpostazione demo, l'utilizzo della memoria è molto simile all'utilizzo dopo la prima esecuzione. Ad esempio:

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

Il set di risultati è il seguente.

type name pages_MB
MEMORYCLERK_XTP Predefinito 1863
MEMORYCLERK_XTP DB_ID_5 7390
MEMORYCLERK_XTP Predefinito 0
MEMORYCLERK_XTP Predefinito 0

Utilizzo del disco per tabelle ottimizzate per la memoria

Le dimensioni complessive su disco per i file del checkpoint di un database in un determinato momento possono essere recuperate tramite la query seguente:

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';

Stato iniziale

Quando vengono creati inizialmente il filegroup di esempio e le tabelle ottimizzate per la memoria di esempio, vengono creati diversi file di checkpoint e il sistema inizia a compilare i file. Il numero di file di checkpoint creati in precedenza dipende dal numero di processori logici nel sistema. Poiché l'esempio è inizialmente molto piccolo, i file precreati sono prevalentemente vuoti dopo la creazione iniziale.

Il codice seguente include le dimensioni iniziali su disco per l'esempio in un computer con 16 processori logici:

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';

Il set di risultati è il seguente.

Dimensione su disco in MB
2312

Come si può notare, esiste una grande discrepanza tra le dimensioni su disco dei file di checkpoint, ovvero 2,3 GB, e le dimensioni effettive dei dati, che sono più vicine a 30 MB.

Esaminando più da vicino la provenienza dell'utilizzo dello spazio su disco, è possibile usare la query indicata di seguito. La dimensione su disco restituita dalla query è approssimativa per i file con lo stato in 5 (REQUIRED FOR BACKUP/HA), 6 (IN TRANSITION TO TOMBSTONE) o 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;

Per lo stato iniziale dell'esempio, il risultato è simile alla tabella seguente per un server con 16 processori logici:

descrizione_stato descrizione_tipo_file numero dimensione su disco MB
PRECREATO DATI 16 2048
PRECREATO DELTA 16 128
IN COSTRUZIONE DATI 1 128
IN COSTRUZIONE DELTA 1 8

Come si può notare, la maggior parte dello spazio è utilizzato dai file di dati precreati e file differenziali. SQL Server ha precreato una coppia di file (dati, delta) per processore logico. Inoltre, i file di dati vengono preconfigurati a 128 MB e i file differenziali a 8 MB, per rendere più efficiente l'inserimento di dati in questi file.

I dati effettivi nelle tabelle ottimizzate per la memoria si trovano nel singolo file di dati.

Dopo l'esecuzione del carico di lavoro

Dopo una singola esecuzione di test tramite cui vengono inseriti 10 milioni di ordini vendita, le dimensioni complessive su disco saranno simili alle seguenti (per un server di prova con 16 core):

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';

Il set di risultati è il seguente.

Dimensione su disco in MB
8828

Le dimensioni su disco sono prossime ai 9 GB, che sono simili a quelle delle dimensioni in memoria dei dati.

Esaminando più da vicino le dimensioni dei file di checkpoint nei vari stati:

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;

Il set di risultati è il seguente.

state_desc file_type_desc count on-disk size MB
PRECREATO DATI 16 2048
PRECREATO DELTA 16 128
IN COSTRUZIONE DATI 1 128
IN COSTRUZIONE DELTA 1 8

Sono ancora presenti 16 coppie di file creati in precedenza, pronti per l'uso man mano che i checkpoint vengono chiusi.

C'è una coppia in costruzione, che viene usata fino alla chiusura del checkpoint corrente. Insieme ai file del checkpoint attivi, ciò comporta un utilizzo di circa 6,5 GB su disco per 6,5 GB di dati in memoria. Tenere presente che gli indici non sono persistenti su disco e quindi le dimensioni complessive del disco sono inferiori alle dimensioni in memoria in questo caso.

Dopo il reset della demo

Dopo la reimpostazione demo, lo spazio su disco non viene recuperato immediatamente se non è presente alcun carico di lavoro transazionale nel sistema e non sono presenti checkpoint del database. Affinché i file di checkpoint vengano spostati nelle varie fasi e alla fine vengano rimossi, è necessario che si verifichino diversi checkpoint ed eventi di troncamento del log, che si avvii l'unione dei file di checkpoint e che si avvii l'operazione di Garbage Collection. Questi si verificano automaticamente se si dispone di un carico di lavoro transazionale nel sistema (ed eseguono backup regolari del log, nel caso in cui si usi il modello di recupero FULL), ma non quando il sistema è inattivo, come in uno scenario demo.

Nell'esempio, dopo la reimpostazione demo, potrebbe essere visualizzato un aspetto simile al seguente:

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';

Il set di risultati è il seguente.

Dimensione su disco in MB
11839

Quasi 12 GB, che sono significativamente maggiori dei 9 GB disponibili prima della reimpostazione della dimostrazione. Ciò è dovuto al fatto che sono state avviate alcune unioni di file di checkpoint, ma alcune delle destinazioni di merge non sono ancora state installate e alcuni dei file di origine di merge non sono ancora stati puliti, come illustrato nell'esempio seguente:

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;

Il set di risultati è il seguente.

state_desc file_type_desc count on-disk size MB
PRECREATO DATI 16 2048
PRECREATO DELTA 16 128
ATTIVO DATI 38 5152
ATTIVO DELTA 38 1331
OBIETTIVO DI UNIONE DATI 7 896
OBIETTIVO DI UNIONE DELTA 7 56
FONTE UNIFICATA DATI 13 1772
FONTE UNIFICATA DELTA 13 455

Le destinazioni di fusione vengono installate e le origini di fusione vengono rimosse quando viene eseguita l'attività transazionale nel sistema.

Dopo una seconda esecuzione del carico di lavoro demo, inserendo 10 milioni di ordini di vendita dopo la reimpostazione demo, si noterà che i file costruiti durante la prima esecuzione del carico di lavoro sono stati puliti. Se si esegue la query precedente più volte mentre il carico di lavoro è in esecuzione, è possibile vedere i file di checkpoint fare il loro percorso attraverso le varie fasi.

Dopo la seconda esecuzione del carico di lavoro e dopo aver inserito 10 milioni di ordini di vendita, si noterà che l'utilizzo del disco potrebbe essere molto simile, anche se non necessariamente uguale, a quello dopo la prima esecuzione, poiché il sistema è per sua natura dinamico. Ad esempio:

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;

Il set di risultati è il seguente.

state_desc file_type_desc count on-disk size MB
PRECREATO DATI 16 2048
PRECREATO DELTA 16 128
IN COSTRUZIONE DATI 2 268
IN COSTRUZIONE DELTA 2 16
ATTIVO DATI 41 5608
ATTIVO DELTA 41 328

In questo caso, sono presenti due coppie di file di checkpoint nello UNDER CONSTRUCTION stato, il che significa che più coppie di file sono state spostate nello UNDER CONSTRUCTION stato, probabilmente a causa dell'elevato livello di concorrenza nel carico di lavoro. Più thread simultanei richiedono una nuova coppia di file contemporaneamente e quindi spostano una coppia da PRECREATED a UNDER CONSTRUCTION.