Condividi tramite


Database di esempio per OLTP in memoria

Si applica a:SQL Serverdatabase SQL di Azure

Panoramica

Questo esempio illustra la funzionalità OLTP in memoria . Mostra le tabelle ottimizzate per la memoria e le stored procedure compilate nativamente, e può essere utilizzato per dimostrare i vantaggi delle prestazioni di In-Memory OLTP.

Nota

Per visualizzare questo argomento per SQL Server 2014 (12.x), vedi Estensioni a AdventureWorks per illustrare OLTP in memoria.

Nell'esempio viene eseguita la migrazione di cinque tabelle del database AdventureWorks2022 a tabelle ottimizzate per la memoria. Inoltre, è incluso un carico di lavoro dimostrativo di ordini di vendita. Questo carico di lavoro dimostrativo può essere usato per verificare i vantaggi a livello di prestazioni di OLTP in memoria nel server.

La descrizione dell'esempio prende in esame i compromessi raggiunti quando si esegue 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: Hardware considerations for In-Memory OLTP in SQL Server 2014 (Considerazioni sull'hardware per OLTP in memoria in SQL Server 2014)

Installazione dell'esempio di 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 del file di dati, ad esempio

      'h:\DATA\AdventureWorks2022_Data.mdf'

    2. Identificare la cartella di destinazione e il nome del 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 e il carico di lavoro di esempio, decomprimi il file SQLServer2016Samples.zip in una cartella locale. Per istruzioni sull'esecuzione del carico di lavoro, consultare il file In-Memory OLTP\readme.txt.

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 AdventureWorks2022. Lo schema delle nuove tabelle è simile a quello delle tabelle esistenti, con alcune differenze, come illustrato di seguito.

Nelle nuove tabelle ottimizzate per la memoria è incluso il suffisso '_inmem'. Nell'esempio sono incluse anche tabelle corrispondenti con il suffisso "_ondisk". Queste tabelle possono essere usate per un confronto uno-a-uno tra le prestazioni delle tabelle ottimizzate per la memoria e quelle 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 un 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 AdventureWorks2022 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, nelle nuove tabelle introdotte con questo esempio vengono utilizzate le stesse colonne e gli stessi tipi di dati delle tabelle originali. Tuttavia, esistono alcune differenze elencate di seguito, insieme a una spiegazione logica delle 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. L'ora di sistema è un esempio di risorsa globale e si è notato che può costituire un collo di bottiglia quando si esegue un carico di lavoro di OLTP in memoria che inserisce ordini di vendita, in particolare se l'ora di sistema deve essere recuperata per più colonne nell'intestazione dell'ordine di vendita, nonché per i 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 il tipo alias definito dall'utente (UDT) per le tabelle ottimizzate per la memoria, pertanto nelle nuove tabelle vengono utilizzati rispettivamente i tipi di dati di sistema nvarchar(25) e nvarchar(15).

  • Colonne che ammettono i valori Null nelle chiavi di indice. Nella tabella originale la colonna SalesPersonID ammette i valori Null, mentre nelle nuove tabelle non ammette i valori Null e prevede un vincolo predefinito con valore (-1). La circostanza è dovuta al fatto che gli indici nelle tabelle ottimizzate per la memoria non possono disporre di colonne che ammettono i valori Null nella chiave di indice; -1 è un surrogato di NULL in questo caso.

  • Colonne calcolate. Le colonne calcolate SalesOrderNumber e TotalDue vengono omesse, poiché in SQL Server 2016 (13.x) non sono supportate 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) CTP 1.1.
      A partire da SQL Server 2017 (14.x) CTP 1.1, le colonne calcolate sono supportate in indici e tabelle ottimizzate 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 attiva nel carico di lavoro di esempio e i vincoli di chiavi esterne comportano un'ulteriore elaborazione per tutte le operazioni DML, in quanto sono necessarie ricerche in tutte le altre tabelle a cui viene fatto riferimento in questi vincoli. Pertanto, si presuppone che l'applicazione garantisca l'integrità referenziale per la tabella Sales.SalesOrderHeader_inmem e questa integrità non venga convalidata quando vengono inserite le righe.

  • Rowguid . La colonna rowguid viene omessa. Anche se uniqueidentifier è 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, non viene eseguita la migrazione del vincolo predefinito per cui sono richieste data/ora del sistema. L'inserimento della data/ora di sistema corrente verrà eseguito dalla stored procedure tramite cui vengono inseriti gli ordini di vendita al primo inserimento.

  • Colonne calcolate: la migrazione della colonna calcolata LineTotal non è stata eseguita poiché le colonne di questo tipo non sono supportate con le tabelle ottimizzate per la memoria in SQL Server 2016 (13.x). Per accedere a questa colonna, usa la vista Sales.vSalesOrderDetail_extended_inmem.

  • 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 le analisi dell'intervallo, le analisi ordinate o la ricerca nelle colonne iniziali della chiave di indice. Di conseguenza, prestare attenzione quando si utilizzano questi indici. Inoltre, è necessario specificare il 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 migrate sono stati ottimizzati per l'elaborazione dimostrativa degli ordini di vendita. 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 specificato non contiene molti venditori. 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 specificato non include molti clienti, ma tiene conto della 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 in (SalesOrderID, SalesOrderDetailID) saranno rare, l'utilizzo di un indice hash per la chiave rende più veloce gli inserimenti di riga. 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 per ordine di vendita sono frequenti; vorrai trovare tutte le voci di linea 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 specificato non include molti prodotti, ma tiene conto della 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): tramite esso saranno possibili scansioni ordinate dei nomi dei prodotti

  • Indice NONCLUSTERED su (ProductNumber): tramite esso saranno possibili scansioni ordinate dei numeri dei prodotti

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.

Nel carico di lavoro dimostrativo non viene fatto riferimento a Sales.SpecialOfferProduct_inmem, di conseguenza non vi sono esigenze apparenti di usare indici hash in questa tabella per ottimizzare il carico di lavoro; gli indici in (SpecialOfferID, ProductID) e (ProductID) sono NONCLUSTERED.

Si noti che nelle indicazioni sopra riportate, ad alcuni dei bucket_counts è stato applicato un ridimensionamento eccessivo, ma non ai bucket_counts per gli indici in SalesOrderHeader_inmem e SalesOrderDetail_inmem che vengono ridimensionati solo a 10 milioni di ordini vendita. Questa operazione è stata eseguita per consentire l'installazione del campione sui sistemi con scarsa disponibilità di memoria, anche se in quei casi il carico di lavoro della demo andrà in errore per mancanza di memoria. 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.

Stored Procedures 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 dettagli relativi all'intestazione dell'ordine di vendita nonché alle sue voci di dettaglio.

    • Parametro di output:

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

      • @DueDate datetime2

      • @CustomerID int

      • @BillToAddressID [int]

      • @ShipToAddressID [int]

      • @ShipMethodID [int]

      • @SalesOrderDetails Sales.SalesOrderDetailType_inmem - parametro con valori di tabella (TVP) che contiene le voci dell'ordine

    • Parametri di input (facoltativi):

      • @Status [tinyint]

      • @OnlineOrderFlag [bit]

      • @PurchaseOrderNumber [nvarchar](25)

      • @AccountNumber [nvarchar](15)

      • @SalesPersonID [int]

      • @TerritoryID [int]

      • @CreditCardID [int]

      • @CreditCardApprovalCode [varchar](15)

      • @CurrencyRateID [int]

      • @Comment nvarchar(128)

  • Sales.usp_UpdateSalesOrderShipInfo_inmem

    • Aggiornare le informazioni sulla spedizione per un ordine vendita specificato. Verranno aggiornate anche le informazioni sulla spedizione di tutte le voci dell'ordine 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. È destinato a essere chiamato dalla stored procedure 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 cui 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 AdventureWorks2022.

    • 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 utilizzando il carico di lavoro dimostrativo

Ostress è uno strumento da riga di comando sviluppato dal team di supporto 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 un'istruzione T-SQL fornita in parallelo e specificare il numero di esecuzioni dell'istruzione in questo thread. Tramite ostress viene eseguita l'accelerazione dei thread e l'istruzione viene eseguita in tutti i thread in parallelo. Al termine dell'esecuzione di tutti i thread, tramite ostress verrà segnalato il tempo impiegato per il completamento dell'esecuzione di tutti i thread.

Installazione di ostress

Ostress viene installato come parte delle utilità Report Markup Language (RML). La relativa installazione non viene eseguita in modalità autonoma.

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 viene visualizzata una finestra di dialogo indicante l'utilizzo di determinati file, seleziona 'Continua'

Esecuzione di ostress

Ostress viene eseguito dal prompt della riga di comando. È più semplice 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. Seleziona il prompt dei comandi RML, che sarà disponibile 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 eseguendo semplicemente ostress.exe senza opzioni della riga di comando. Le opzioni principali da considerare per eseguire ostress con questo esempio sono:

  • -S. Nome dell'istanza di SQL Server a cui connettersi

  • -E. Usare l'autenticazione di Windows per la connessione (valore predefinito); se si usa l'autenticazione di SQL Server, usare le opzioni -U e -P per specificare rispettivamente il nome utente e la password

  • -d. Nome del database, per questo esempio AdventureWorks2022

  • -Q. L'istruzione T-SQL da eseguire

  • -n. Numero di connessioni tramite cui viene eseguita l'elaborazione di ogni query o file di input

  • -r il numero di iterazioni per ogni connessione per eseguire ogni file di input/query

Carico di lavoro dimostrativo

La procedura archiviata principale utilizzata per l'attività dimostrativa è Sales.usp_InsertSalesOrder_inmem/ondisk. Lo script riportato di seguito consente di costruire 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 utilizzato per eseguire le chiamate delle stored procedure in parallelo, per simulare i client inserendo contemporaneamente gli ordini di vendita.

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

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. Negli ambienti di produzione tipici, tramite l'applicazione di livello intermedio verrà costruito l'ordine vendita da inserire.

Tramite lo script precedente gli ordini vendita vengono inseriti nelle tabelle ottimizzate per la memoria. Lo script per inserire gli ordini vendita nelle tabelle basate su disco deriva dalla sostituzione di due occorrenze di '_inmem' con '_ondisk'.

Si utilizzerà lo strumento ostress per eseguire gli script utilizzando diverse connessioni simultanee. Si utilizzerà il parametro '-n' per controllare il numero di connessioni e il parametro 'r' per controllare il numero di volte in cui lo script viene eseguito in ogni connessione.

Esecuzione del 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, vedi la sezione Risoluzione dei problemi relativi ai test con esecuzione prolungata. Per ridurre il livello di stress per questo test, riduci il numero di connessioni modificando il parametro '-n'. Ad esempio, per abbassare il numero di connessioni a 40, impostare il parametro '-n100' su '-n40'.

Come misura delle prestazioni del carico di lavoro è possibile usare il tempo trascorso come riportato da ostress.exe dopo l'esecuzione del carico di lavoro.

Le istruzioni e le metriche seguenti utilizzano un carico di lavoro che inserisce 10 milioni di ordini di vendita. Per le istruzioni su come eseguire un carico di lavoro in scala ridotta inserendo 1 milione di ordini di vendita, vedi le istruzioni in "In-Memory OLTP\readme.txt" che fa parte dell'archivio SQLServer2016Samples.zip.

Tabelle ottimizzate per la memoria

Si inizierà eseguendo 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 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 int = 0, @od Sales.SalesOrderDetailType_inmem, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID int = (rand() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID= cast((rand()*106) + 1 as int); while (@i < 20) begin; EXEC Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"  

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 al 100%. In caso contrario, si avrà un collo di bottiglia a livello di operazioni di I/O del log. Vedere anche Risoluzione dei problemi relativi ai test con esecuzione prolungata.

Tabelle basate su disco

Tramite il comando riportato di seguito verrà eseguito il carico di lavoro nelle tabelle basate su disco. L'esecuzione di questo carico di lavoro può richiedere del tempo, in gran parte a causa di una contesa di latch nel sistema. Le tabelle ottimizzate per la memoria sono prive di latch e quindi 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 int = 0, @od Sales.SalesOrderDetailType_ondisk, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID int = (rand() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID= cast((rand()*106) + 1 as int); while (@i < 20) begin; EXEC Sales.usp_InsertSalesOrder_ondisk @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"  

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 della differenza a livello di prestazioni tra le tabelle ottimizzate per la memoria e quelle basate su disco in questo test è che quando si usano le 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 di OLTP in memoria è privo di latch e le righe di dati non sono organizzate in pagine. Di conseguenza, le transazioni simultanee non impediscono inserimenti reciproci, consentendo in questo modo l'uso completo della CPU da parte di SQL Server.

È 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 dal 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.

Reimpostazione della dimostrazione

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 può richiedere alcuni minuti.

Si consiglia una reimpostazione dopo ogni esecuzione della dimostrazione. Poiché questo carico di lavoro è caratterizzato solo da operazioni di inserimento, per ogni esecuzione verrà utilizzata più memoria e, pertanto, per impedire l'esaurimento di quest'ultima sarà necessario eseguire una reimpostazione. La quantità di memoria utilizzata dopo un'esecuzione è descritta nella sezione Utilizzo della memoria dopo l'esecuzione del carico di lavoro.

Risoluzione dei problemi relativi ai test a esecuzione lenta

I risultati dei test variano in genere a seconda dell'hardware e del livello di concorrenza utilizzato durante l'esecuzione del test. Se i risultati non sono quelli previsti, è opportuno verificare alcune informazioni:

  • Numero di transazioni simultanee. Quando si esegue il carico di lavoro in un solo thread, il miglioramento delle prestazioni con OLTP in memoria sarà probabilmente minore di 2 volte. La contesa di latch è un problema significativo solo se vi è un livello elevato di concorrenza.

  • Numero contenuto di core disponibili in SQL Server. Ciò significa che vi sarà un livello basso di concorrenza nel sistema, dal momento che vi possono essere tante esecuzioni simultanee di transazioni quanti sono i core disponibili in SQL.

    • Sintomo: se l'utilizzo della CPU è elevato durante l'esecuzione di carichi di lavoro su tabelle basate su disco, significa che non c'è molta contesa, indicando una mancanza di concorrenza.
  • Velocità dell'unità dei log: se l'unità dei log non riesce a tenere il passo con il throughput delle transazioni nel sistema, il carico di lavoro viene limitato dall'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 durante l'esecuzione del carico di lavoro nelle tabelle ottimizzate per la memoria, è possibile la presenza di 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 della memoria e dello spazio su disco nell'esempio

Di seguito viene descritto cosa ci si aspetta in termini di utilizzo della memoria e dello spazio su disco per il database di esempio. Vengono inoltre illustrati i risultati ottenuti in un server di prova 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 (database_id potrebbe differire nell’istanza in uso), è pari a 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 dms JOIN sys.tables 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 memoria_assegnata_alla_tabella_kb memoria_allocata_per_indici_kb
ProdottoOffertaSpeciale_inmem 64 3840
DemoSalesOrderHeaderSeed 1984 5504
SalesOrderDetail_inmem 15316 663552
DemoDettaglioOrdineVenditeSeed 64 10432
OffertaSpeciale_inmem 3 8192
SalesOrderHeader_inmem 7168 147456
Product_inmem 124 12352

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

Ciò che colpisce qui sono le dimensioni della memoria allocata per gli indici, rispetto alle dimensioni dei dati della tabella. Questa condizione è dovuta al fatto che gli indici hash nell'esempio vengono ridimensionati preventivamente per una dimensione più ampia dei dati. Si noti che gli indici hash hanno dimensioni fisse e pertanto non aumenteranno 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 risulta simile a quanto segue:

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'  
tipo nome pagine_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 dms JOIN sys.tables t   
ON dms.object_id=t.object_id  
WHERE t.type='U'  
Nome della tabella memory_allocated_for_table_kb memoria_allocata_per_indici_kb
SalesOrderDetail_inmem 5113761 663552
DemoSalesOrderDetailSeed 64 10368
SpecialOffer_inmem 2 8192
SalesOrderHeader_inmem 1575679 147456
Product_inmem 111 12032
SpecialOfferProduct_inmem 64 3712
DemoSalesOrderHeaderSeed 1984 5504

Si può notare un totale pari a circa 6,5 GB di dati. Si noti che le dimensioni degli indici delle tabelle SalesOrderHeader_inmem e SalesOrderDetail_inmem equivalgono alle dimensioni degli indici prima dell'inserimento degli ordini vendita. Le dimensioni dell'indice non sono state modificate perché in entrambe le tabelle vengono utilizzati indici hash e questo tipo di indice è statico.

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_inmem, quindi esegue nuovamente il seeding dei dati dalle tabelle originali SalesOrderHeader e SalesOrderDetail.

A questo punto, anche se le righe nelle tabelle sono state eliminate, questo 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 subito dopo la reimpostazione della demo, senza alcun carico di lavoro transazionale sul sistema, la memoria delle righe eliminate non è ancora stata recuperata.

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%';
tipo nome pagine_MB
MEMORYCLERK_XTP Predefinito 2261
MEMORYCLERK_XTP DB_ID_5 7396
MEMORYCLERK_XTP Predefinito 0
MEMORYCLERK_XTP Predefinito 0

Si prevede che la memoria verrà recuperata quando il carico di lavoro transazionale è in esecuzione.

Se si avvia una seconda esecuzione del carico di lavoro dimostrativo, si noterà inizialmente una diminuzione dell'utilizzo della memoria, dal momento che le righe eliminate in precedenza vengono rimosse. A un certo punto le dimensioni della memoria aumenteranno di nuovo, finché il carico di lavoro non viene completato. Dopo l'inserimento di 10 milioni di righe al termine della reimpostazione della dimostrazione, l'utilizzo della memoria sarà 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%';
tipo nome 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 f JOIN sys.database_files 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 preventivamente un numero di file del checkpoint e, tramite il sistema viene iniziata la compilazione di questi file. Il numero di file del checkpoint creati in precedenza dipende dal numero di processori logici nel sistema. Poiché il campione è inizialmente molto piccolo, i file creati in precedenza saranno per la maggior parte 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 f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX';  
Dimensione su disco in MB
2312

Come si può notare, esiste una grande discrepanza tra le dimensioni su disco dei file del checkpoint, vale a dire 2,3 GB, e le dimensioni effettive dei dati, prossime 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 sarà simile 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 pre-creato una coppia di file (dati, delta) per processore logico. Inoltre, i file di dati vengono dimensionati in precedenza a 128 MB, mentre quelli differenziali a 8 MB, per rendere più efficiente l'inserimento dei 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 f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX';  
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;
descrizione dello 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

Si dispone ancora di 16 coppie di file creati in precedenza, pronti all'utilizzo alla chiusura dei checkpoint.

È disponibile una coppia in fase di costruzione, che viene utilizzata fino a quando il checkpoint corrente non viene chiuso. 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. Si tenta presente che gli indici non vengono salvati in modo persistente su disco e pertanto, in questo caso, le dimensioni complessive su disco sono inferiori rispetto a quelle in memoria.

Dopo la reimpostazione della demo

Dopo la reimpostazione della dimostrazione, lo spazio su disco non viene recuperato immediatamente se non vi è un carico di lavoro transazionale nel sistema e non vi sono checkpoint del database. Perché i file di checkpoint possano essere spostati attraverso le varie fasi e infine rimossi, devono verificarsi diversi checkpoint ed eventi di troncamento del log, per avviare l'unione dei file di checkpoint e il processo di raccolta dei rifiuti. Ciò si verifica automaticamente se si dispone di un carico di lavoro transazionale nel sistema, e vengono eseguiti backup regolari del log, qualora venga utilizzato il modello di recupero con registrazione completa, ma non quando il sistema è inattivo, come in uno scenario dimostrativo.

Nell'esempio, dopo la reimpostazione della dimostrazione, è possibile che si ottenga un risultato simile al seguente:

SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]  
FROM sys.filegroups f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX';
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 operazioni di unione di file del checkpoint, ma alcune destinazioni delle unioni non sono ancora state installate e alcuni dei file di origine di unione non sono ancora stati rimossi, come si può notare da quanto riportato di seguito:

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;
descrizione_stato descrizione_tipo_file numero dimensione su disco MB
PRECREATO DATI 16 2048
PRECREATO DELTA 16 128
ATTIVO DATI 38 5152
ATTIVO DELTA 38 1331
OBIETTIVO DI UNIONE DATI 7 896
MERGE TARGET DELTA 7 56
FONTE UNIFICATA DATI 13 1772
FONTE UNITA DELTA 13 455

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

Al termine di una seconda esecuzione del carico di lavoro dimostrativo, inserendo 10 milioni di ordini vendita dopo la reimpostazione della dimostrazione, si noterà che i file creati durante la prima esecuzione del carico di lavoro sono stati rimossi. Se si esegue la query sopra indicata più volte durante l'esecuzione del carico di lavoro, è possibile visualizzare i file di checkpoint mentre attraversano le varie fasi.

Dopo la seconda esecuzione del carico di lavoro con l'inserimento di 10 milioni di ordini di vendita, si noterà un utilizzo del disco molto simile, sebbene non necessariamente identico a quello dopo la prima esecuzione, poiché il sistema è di natura dinamica. 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;
descrizione dello stato file_type_desc numero dimensione su disco MB
PRE-CREATO 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, ci sono due coppie di file di checkpoint nello stato 'in costruzione', il che significa che più coppie di file sono state trasferite allo stato 'in costruzione' probabilmente a causa dell'elevato livello di concorrenza del workload. Più thread simultanei hanno richiesto una nuova coppia di file contemporaneamente e di conseguenza una coppia è passata dallo stato di 'precreato' a quello di 'in costruzione'.

Passaggi successivi