Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier les répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer de répertoire.
S’applique à :SQL Server
Azure SQL Database
Vue d’ensemble
Cet exemple présente la fonctionnalité OLTP en mémoire. Il affiche des tables optimisées en mémoire et des procédures stockées compilées en mode natif et peut être utilisée pour illustrer les avantages en matière de performances d’OLTP en mémoire.
Remarque
Pour afficher cet article pour SQL Server 2014 (12.x), consultez Extensions d'AdventureWorks pour démontrer In-Memory OLTP.
L’exemple permet de migrer cinq tables de la base de données AdventureWorks2025 vers des tables à mémoire optimisée. De plus, il comporte une charge de travail de démonstration pour le traitement des commandes client. Vous pouvez utiliser cette charge de travail de démonstration pour voir les performances de l’utilisation d’OLTP en mémoire sur votre serveur.
Dans la description de l’exemple, nous abordons les compromis qui ont été effectués lors de la migration des tables vers OLTP en mémoire pour tenir compte des fonctionnalités qui ne sont pas (encore) prises en charge pour les tables mémoire optimisées.
La documentation de l'exemple est structurée comme suit :
Configuration requise pour installer l’exemple et exécuter la charge de travail de démonstration.
Instructions pour installer l’exemple OLTP In-Memory basé sur AdventureWorks.
Description des exemples de tables et de procédures : inclut des descriptions des tables et procédures ajoutées par l’exemple OLTP en mémoire, ainsi que des considérations relatives à
AdventureWorks2025la migration de certaines des tables d’origineAdventureWorks2025afin qu’elles soient optimisées en mémoire.Instructions pour effectuer des mesures de performances à l’aide de la charge de travail de démonstration : inclut des instructions pour l’installation et l’exécution d’ostress, un outil qui permet de piloter la charge de travail et d’exécuter la charge de travail de démonstration elle-même.
Utilisation de la mémoire et de l’espace disque dans l’exemple.
Prérequis
-
SQL Server 2016 (13.x)
Pour tester les performances, un serveur avec des caractéristiques semblables dans votre environnement de production. Pour cet exemple en particulier, vous devez disposer d’au moins 16 Go de mémoire pour SQL Server. Pour obtenir des instructions générales sur le matériel pour OLTP en mémoire, consultez le billet de blog suivant : Considérations relatives au matériel pour In-Memory OLTP dans SQL Server
Installer l’exemple OLTP en mémoire basé sur AdventureWorks
Procédez comme suit pour installer l'exemple :
Téléchargez
AdventureWorks2016_EXT.baketSQLServer2016Samples.zipà partir de : https://github.com/microsoft/sql-server-samples/releases/tag/adventureworks dans un dossier local, par exempleC:\Temp.Restaurez la sauvegarde de base de données en utilisant Transact-SQL ou SQL Server Management Studio :
Identifiez le dossier cible et le nom de fichier de données, par exemple :
H:\DATA\AdventureWorks2022_Data.mdfIdentifiez le dossier cible et le nom de fichier du fichier journal, par exemple :
I:\DATA\AdventureWorks2022_log.ldf- Le fichier journal doit être placé sur un lecteur différent du fichier de données, idéalement un lecteur à faible latence tel qu'un stockage sur disque SSD ou PCIe, pour des performances maximales.
Exemple de script T-SQL :
RESTORE DATABASE [AdventureWorks2022] FROM DISK = N'C:\temp\AdventureWorks2022.bak' WITH FILE = 1, MOVE N'AdventureWorks2022_Data' TO N'h:\DATA\AdventureWorks2022_Data.mdf', MOVE N'AdventureWorks2022_Log' TO N'i:\DATA\AdventureWorks2022_log.ldf', MOVE N'AdventureWorks2022_mod' TO N'h:\data\AdventureWorks2022_mod' GOPour afficher les exemples de scripts et la charge de travail, décompressez le fichier
SQLServer2016Samples.zipdans un dossier local. Consultez le fichierIn-Memory OLTP\readme.txtpour obtenir des instructions sur l’exécution de la charge de travail.
Description des exemples de tables et de procédures
L’exemple crée de nouvelles tables pour les produits et les commandes client en fonction des tables présentes dans AdventureWorks2025. Le schéma des nouvelles tables est similaire aux tables existantes, avec quelques différences, comme expliqué plus loin dans cette section.
Les nouvelles tables optimisées en mémoire portent le suffixe _inmem. L’exemple inclut également des tables correspondantes portant le suffixe _ondisk . ces tables peuvent être utilisées pour effectuer une comparaison un-à-un entre les performances des tables mémoire optimisées et des tables sur disque sur votre système.
Les tables à mémoire optimisée utilisées dans la charge de travail pour la comparaison du niveau de performance sont entièrement durables et entièrement journalisées. Ils ne sacrifient pas la durabilité ou la fiabilité pour atteindre le gain de performance.
La charge de travail cible pour cet exemple est le traitement des commandes, comprenant également des informations sur les produits et les remises. À cette fin, nous utilisons les tables SalesOrderHeader, SalesOrderDetail, Product, SpecialOffer et SpecialOfferProduct.
Deux nouvelles procédures stockées, Sales.usp_InsertSalesOrder_inmem et Sales.usp_UpdateSalesOrderShipInfo_inmem, sont utilisées pour insérer les commandes client et mettre à jour les informations d’expédition d’une commande spécifique.
Le nouveau schéma Demo contient les tables d’assistance et les procédures stockées permettant d’exécuter une charge de travail de démonstration.
Concrètement, l’exemple In-Memory OLTP ajoute les objets suivants dans AdventureWorks2025 :
Tables ajoutées par l'exemple
Nouvelles tables
Sales.SalesOrderHeader_inmem
- Informations sur les en-têtes des commandes. Chaque commande possède une ligne dans cette table.
Sales.SalesOrderDetail_inmem
- Détails des commandes. À chaque article d'une commande correspond une ligne dans cette table.
Sales.SpecialOffer_inmem
- Informations sur les offres spéciales, y compris le pourcentage de remise associé à chaque offre spéciale.
Sales.SpecialOfferProduct_inmem
- Table de référence qui relie les offres spéciales et les produits. Chaque offre spéciale peut contenir zéro ou plusieurs produits, et chaque produit peut être associé à zéro ou plusieurs offres spéciales.
Production.Product_inmem
- Informations sur les produits, notamment leur prix catalogue.
Demo.DemoSalesOrderDetailSeed
- Utilisé dans la charge de travail de démonstration pour construire des exemples de commandes.
Variations sur disque des tables :
Sales.SalesOrderHeader_ondiskSales.SalesOrderDetail_ondiskSales.SpecialOffer_ondiskSales.SpecialOfferProduct_ondiskProduction.Product_ondisk
Différences entre les tables sur disque d’origine et les nouvelles tables à mémoire optimisée
En règle générale, les nouvelles tables introduites par cet exemple utilisent les mêmes colonnes et les mêmes types de données que les tables d’origine. Toutefois, il existe quelques différences. Nous listons les différences dans cette section, ainsi qu’une justification des modifications.
Sales.SalesOrderHeader_inmem
Lescontraintes par défaut sont prises en charge pour les tables optimisées en mémoire, et la plupart des contraintes par défaut ont été migrées en l’état. Toutefois, la table
Sales.SalesOrderHeaderd’origine contient plusieurs contraintes par défaut qui récupèrent la date actuelle, pour les colonnesOrderDateetModifiedDate. Dans une charge de travail de traitement des commandes à haut débit avec de nombreuses concurrences, toute ressource globale peut devenir un point de contention. Le temps système est une ressource globale, et nous avons observé qu’il peut devenir un goulot d’étranglement lors de l’exécution d’une charge de travail In-Memory OLTP qui insère des commandes client, en particulier si le temps système doit être récupéré pour plusieurs colonnes dans l’en-tête de commande client et les détails de la commande client. Le problème est résolu dans cet exemple en récupérant l’heure système une seule fois pour chaque commande client insérée, puis en utilisant cette valeur pour les colonnes DateHeure dansSalesOrderHeader_inmemetSalesOrderDetail_inmem, dans la procédure stockéeSales.usp_InsertSalesOrder_inmem.Types de données définis par l’utilisateur (UDT, User-Defined Type) alias : la table d’origine utilise deux UDT alias, respectivement
dbo.OrderNumberetdbo.AccountNumberpour les colonnesPurchaseOrderNumberetAccountNumber. SQL Server 2016 (13.x) ne prend pas en charge les alias UDT pour les tables à mémoire optimisée. Les nouvelles tables utilisent respectivement les types de données système nvarchar(25) et nvarchar(15).Colonnes nullables dans les clés d’index : dans la table d’origine, la colonne
SalesPersonIDest nullable, tandis que dans les nouvelles tables, la colonne n’est pas nullable et a une contrainte par défaut avec valeur (-1). Cette circonstance est due au fait que les index sur les tables mémoire optimisées ne peuvent pas avoir de colonnes nullables dans la clé d’index ; -1 est une substitution pour NULL dans ce cas.Colonnes calculées : les colonnes
SalesOrderNumbercalculées etTotalDuesont omises, car SQL Server 2016 (13.x) ne prend pas en charge les colonnes calculées dans les tables mémoire optimisées. La nouvelle vueSales.vSalesOrderHeader_extended_inmemreflète les colonnesSalesOrderNumberetTotalDue. Par conséquent, vous pouvez utiliser cette vue si ces colonnes sont nécessaires.- S’applique à : SQL Server 2017 (14.x). À compter de SQL Server 2017 (14.x), les colonnes calculées sont prises en charge dans les tables et index à mémoire optimisée.
Les contraintes de clé étrangère sont prises en charge pour les tables optimisées en mémoire dans SQL Server 2016 (13.x), mais uniquement si les tables référencées sont également optimisées en mémoire. Les clés étrangères qui référencent des tables également migrées vers des tables optimisées en mémoire sont conservées dans les tables migrées, tandis que les autres clés étrangères sont omises. En outre,
SalesOrderHeader_inmemest une table très consultée dans l'exemple de charge de travail, et les contraintes de clés étrangères nécessitent un traitement supplémentaire pour toutes les opérations DML, car elles nécessitent des recherches dans toutes les autres tables référencées dans ces contraintes. Par conséquent, l’hypothèse est que l’application garantit l’intégrité référentielle de laSales.SalesOrderHeader_inmemtable et que l’intégrité référentielle n’est pas validée lorsque les lignes sont insérées.Rowguid : la colonne ROWGUID est omise. Bien que uniqueidentifier soit pris en charge pour les tables optimisées en mémoire, l’option ROWGUIDCOL n’est pas prise en charge dans SQL Server 2016 (13.x). Les colonnes de ce type sont généralement utilisées pour la réplication de fusion ou pour des tables qui possèdent des colonnes FILESTREAM. Cet exemple ne comporte aucun de ces éléments.
Sales.SalesOrderDetail
Contraintes par défaut : similaires à
SalesOrderHeader, la contrainte par défaut nécessitant la date/heure système n’est pas migrée. Au lieu de cela, la procédure stockée qui insère des commandes commerciales s’occupe de l’insertion de la date/heure système actuelle lors de la première insertion.Colonnes calculées : la colonne
LineTotalcalculée n’a pas été migrée, car les colonnes calculées ne sont pas prises en charge avec les tables optimisées en mémoire dans SQL Server 2016 (13.x). Pour accéder à cette colonne, utilisez la vueSales.vSalesOrderDetail_extended_inmem.Rowguid : la colonne
rowguidest omise. Pour plus d’informations, consultez la description de la tableSalesOrderHeader.
Production.Product
UDT alias : la table d’origine utilise le type de données défini par l’utilisateur
dbo.Flag, qui est équivalent au bit de type de données système. La table migrée utilise le type de données bit à la place.Rowguid : la colonne
rowguidest omise. Pour plus d’informations, consultez la description de la tableSalesOrderHeader.
Sales.SpecialOffer
-
Rowguid : la colonne
rowguidest omise. Pour plus d’informations, consultez la description de la tableSalesOrderHeader.
Sales.SpecialOfferProduct
-
Rowguid : la colonne
rowguidest omise. Pour plus d’informations, consultez la description de la tableSalesOrderHeader.
Observations sur les index des tables optimisées en mémoire
L'index de base des tables optimisées en mémoire est l'index non cluster, qui prend en charge les recherches de point (recherche d'index dans le prédicat d'égalité), les analyses de plage (recherche d'index dans l'attribut d'inégalité), les analyses d'index complet, et les analyses triées. En outre, les index non cluster prennent en charge la recherche dans les colonnes de début de la clé d'index. En fait, les index non cluster optimisés en mémoire autorisent toutes les opérations prises en charge par les index non cluster sur disque, à la seule exception des analyses ascendantes. Par conséquent, l'utilisation des index non cluster est un choix sûr pour les index.
Les index de HACHAGE peuvent être utilisés pour optimiser davantage la charge de travail. Ils sont spécialement adaptés aux recherches de point et aux insertions de lignes. Toutefois, il faut considérer qu’ils ne prennent pas en charge les analyses de plage, les analyses ordonnées ou la recherche sur les colonnes clés d’index principales. Par conséquent, leur utilisation est plus délicate. En outre, il est nécessaire de spécifier bucket_count lors de la création. Celui-ci doit généralement correspondre à une valeur comprise entre le nombre de valeurs de clé d'index et son double, mais il peut généralement être surestimé.
Pour plus d'informations :
- Instructions pour les opérations d’index en ligne
- Choix du bucket_count approprié
- Index sur des tables optimisées en mémoire
Les index des tables migrées ont été paramétrés pour la charge de traitement des commandes de vente de démonstration. La charge de travail repose sur les insertions et les recherches de point dans les tables Sales.SalesOrderHeader_inmem et Sales.SalesOrderDetail_inmem. Elle s’appuie également sur les recherches de point sur les colonnes clés primaires dans les tables Production.Product_inmem et Sales.SpecialOffer_inmem.
Sales.SalesOrderHeader_inmem possède trois index, qui sont tous des index de HACHAGE pour des raisons de performances. Aucune analyse triée ni aucune analyse de plage n’est nécessaire pour la charge de travail.
Index de HACHAGE sur (
SalesOrderID) : bucket_count est dimensionné à 10 millions (arrondi à 16 millions), car le nombre estimé de commandes client est de 10 millions.Index de HACHAGE sur (
SalesPersonID) : bucket_count est égal à 1 million. Le jeu de données fourni n’a pas beaucoup de vendeurs. Toutefois, ce grand bucket_count permet une augmentation future. De plus, vous n’êtes pas affecté par une dégradation du niveau de performance pour les recherches de points si le bucket_count est surdimensionné.Index de HACHAGE sur (
CustomerID) : bucket_count est égal à 1 million. Le jeu de données fourni n’a pas beaucoup de clients, mais cela permet une croissance future.
Sales.SalesOrderDetail_inmem possède trois index, qui sont tous des index de HACHAGE pour des raisons de performances. Aucune analyse triée ni aucune analyse de plage n’est nécessaire pour la charge de travail.
Index HASH sur (
SalesOrderID,SalesOrderDetailID) : il s’agit de l’index de clé primaire, et même si les recherches sur (SalesOrderID,SalesOrderDetailID) sont peu fréquentes, l'utilisation d'un index de hachage pour la clé accélère les insertions de lignes. Le bucket_count est dimensionné à 50 millions (arrondi à 67 millions) : le nombre attendu de commandes client est de 10 millions, ce qui correspond à une moyenne de cinq articles par commandeIndex HASH sur (
SalesOrderID) : les recherches par commande sont fréquentes : vous souhaitez rechercher tous les éléments de ligne correspondant à une seule commande. Le Index bucket_count est dimensionné à 10 millions (arrondi à 16 millions), car le nombre estimé de commandes est 10 millions.Index de HACHAGE sur (
ProductID) : bucket_count est égal à 1 million. Le jeu de données fourni n’a pas beaucoup de produits, mais cela permet une croissance future.
Production.Product_inmem comporte trois index :
Index de HACHAGE sur (
ProductID) : les recherches surProductIDse trouvent dans le chemin critique de la charge de travail de démonstration. Il s’agit donc d’un index de hachage.Index NONCLUSTERED activé (
Name) : cela permet des analyses ordonnées des noms de produitsIndex NONCLUSTERED activé (
ProductNumber) : cela permet des analyses ordonnées des numéros de produit
Sales.SpecialOffer_inmem comporte un index de HACHAGE sur (SpecialOfferID) : les recherches de point d’offres spéciales se trouvent dans le chemin critique de la charge de travail de démonstration.
bucket_count est dimensionné à 1 million pour permettre la croissance future.
Sales.SpecialOfferProduct_inmem n’est pas référencé dans la charge de travail de démonstration. Par conséquent, il n’est pas nécessaire d’utiliser des index de hachage sur cette table pour optimiser la charge de travail : les index sur (SpecialOfferID, ProductID) et (ProductID) sont NONCLUSTERED.
Dans l’exemple précédent, certains nombres de compartiments sont surdimensionnés, mais pas ceux des index sur SalesOrderHeader_inmem et SalesOrderDetail_inmem : ils sont dimensionnés pour seulement 10 millions de commandes. Cette opération a été effectuée pour permettre l’installation de l’exemple sur des systèmes à faible disponibilité en mémoire, même si, dans ces cas, la charge de démonstration échoue avec une erreur de mémoire saturée. Si vous voulez dimensionner au-delà de 10 millions de commandes, augmentez le nombre de compartiments en conséquence.
Observations sur l'utilisation de la mémoire
L'utilisation de la mémoire dans la base de données d'exemple, avant et après l'exécution de la charge de travail de démonstration, est décrite dans la section Utilisation de la mémoire pour les tables optimisées en mémoire.
Procédures stockées ajoutées par l’exemple
Les deux procédures stockées clés d'insertion des commandes et de mise à jour des informations d'expédition sont les suivantes :
Sales.usp_InsertSalesOrder_inmemInsère une nouvelle commande client dans la base de données et génère le
SalesOrderIDde cette commande client. En tant que paramètres d’entrée, il prend des informations pour l’en-tête de commande client et les éléments de ligne de cette commande.Paramètre de sortie :
- @SalesOrderID int - l'identifiant pour la commande de vente qui vient d’être insérée
Paramètres d'entrée (obligatoires) :
- @DueDatedatetime2
- @CustomerIDint
- @BillToAddressIDint
- @ShipToAddressIDint
- @ShipMethodIDint
-
Sales.SalesOrderDetailType_inmem@SalesOrderDetails - paramètre table (TVP) qui contient les éléments de ligne de l’ordre
Paramètres d'entrée (facultatifs) :
- @Statustinyint
- @OnlineOrderFlagbit
- @PurchaseOrderNumbernvarchar(25)
- @AccountNumbernvarchar(15)
- @SalesPersonIDint
- @TerritoryIDint
- @CreditCardIDint
- @CreditCardApprovalCodevarchar(15)
- @CurrencyRateIDint
- @Commentnvarchar(128)
Sales.usp_UpdateSalesOrderShipInfo_inmemMet à jour les informations d'expédition d'une commande client spécifique. Cela met également à jour les informations d’expédition pour tous les articles de ligne de la commande.
Il s’agit d’une procédure wrapper pour les procédures stockées compilées en mode natif
Sales.usp_UpdateSalesOrderShipInfo_nativeavec une logique de nouvelle tentative permettant de traiter les conflits potentiels (inattendus) avec des transactions simultanées qui mettent à jour la même commande. Pour plus d’informations, consultez Logique de nouvelle tentative.
Sales.usp_UpdateSalesOrderShipInfo_native- Il s'agit de la procédure stockée compilée en mode natif qui traite effectivement la mise à jour des informations d'expédition. Il est destiné à être appelé à partir de la procédure stockée « wrapper »
Sales.usp_UpdateSalesOrderShipInfo_inmem. Si le client peut traiter les échecs et implémente la logique de nouvelle tentative, vous pouvez appeler cette procédure directement, au lieu d'utiliser la procédure stockée wrapper.
- Il s'agit de la procédure stockée compilée en mode natif qui traite effectivement la mise à jour des informations d'expédition. Il est destiné à être appelé à partir de la procédure stockée « wrapper »
La procédure stockée suivante est utilisée pour la charge de travail de démonstration.
Demo.usp_DemoReset- Réinitialise la démonstration en vidant et en réamorçant les tables
SalesOrderHeaderetSalesOrderDetail.
- Réinitialise la démonstration en vidant et en réamorçant les tables
Les procédures stockées suivantes sont utilisées pour insérer et supprimer des tables optimisées en mémoire tout en garantissant l'intégrité du domaine et l'intégrité référentielle.
Production.usp_InsertProduct_inmemProduction.usp_DeleteProduct_inmemSales.usp_InsertSpecialOffer_inmemSales.usp_DeleteSpecialOffer_inmemSales.usp_InsertSpecialOfferProduct_inmem
Enfin, la procédure stockée suivante est utilisée pour vérifier l'intégrité du domaine et l'intégrité référentielle.
dbo.usp_ValidateIntegrityParamètre facultatif : @object_id - ID de l’objet pour valider l’intégrité pour
Cette procédure s’appuie sur les tables
dbo.DomainIntegrity,dbo.ReferentialIntegrityetdbo.UniqueIntegritypour les règles d’intégrité qui doivent être vérifiées. L’exemple remplit ces tables en fonction des contraintes CHECK, de clé étrangère et d’unicité qui existent pour les tables d’origine dans la base de donnéesAdventureWorks2025.Elle repose sur les procédures d’assistance
dbo.usp_GenerateCKCheck,dbo.usp_GenerateFKCheck, etdbo.GenerateUQCheckpour générer l’instruction T-SQL nécessaire aux vérifications de l’intégrité.
Mesures de performances à l’aide de la charge de travail de démonstration
ostress est un outil en ligne de commande développé par l’équipe du support technique Microsoft CSS SQL Server. Cet outil peut être utilisé pour exécuter des requêtes ou des procédures stockées distantes en parallèle. Vous pouvez configurer le nombre de threads pour exécuter une instruction T-SQL donnée en parallèle, et vous pouvez spécifier le nombre de fois où l’instruction doit être exécutée sur ce thread ; ostress fait tourner les threads et exécute l’instruction sur tous les threads en parallèle. Une fois l’exécution terminée pour tous les threads, ostress signale le temps nécessaire pour que tous les threads terminent l’exécution.
Installer ostress
ostress est installé dans le cadre des utilitaires RML (Report Markup Language) ; il n’existe aucune installation autonome pour ostress.
Étapes d'installation :
Téléchargez et exécutez le package d’installation x64 pour les utilitaires RML à partir de la page suivante : Télécharger RML pour SQL Server
S’il existe une boîte de dialogue indiquant que certains fichiers sont en cours d’utilisation, sélectionnez « Continuer »
Exécuter ostress
Ostress s'exécute à partir de l'invite de ligne de commande. Il est plus pratique d’exécuter l’outil à partir de l'invite de commandes RML, qui est installée dans le cadre des utilitaires RML.
Pour ouvrir l’invite de commandes RML, procédez comme suit :
Sur Windows, ouvrez le menu Démarrer en sélectionnant la clé Windows, puis tapez rml. Sélectionnez RML Cmd Prompt, qui se trouve dans la liste des résultats de la recherche.
Vérifiez que l'invite de commandes se trouve dans le dossier d'installation des utilitaires RML.
Les options de ligne de commande pour ostress apparaissent en exécutant ostress.exe simplement sans options de ligne de commande. Les principales options à prendre en compte pour l’exécution d’ostress avec cet exemple sont les suivantes :
| Choix | Descriptif |
|---|---|
-S |
Nom de l’instance SQL Server à laquelle se connecter. |
-E |
Utilisez l’authentification Windows pour vous connecter (par défaut) ; si vous utilisez l’authentification SQL Server, utilisez les options -U et -P spécifiez respectivement le nom d’utilisateur et le mot de passe. |
-d |
Nom de la base de données, pour cet exemple AdventureWorks2025. |
-Q |
Instruction T-SQL à exécuter. |
-n |
Nombre de connexions traitant chaque fichier/requête d’entrée. |
-r |
Nombre d’itérations pour l’exécution de chaque fichier/requête d’entrée par connexion. |
Charge de travail de démonstration
La procédure stockée principale utilisée dans la charge de travail de démonstration est Sales.usp_InsertSalesOrder_inmem/ondisk. Le script de l’exemple suivant construit un paramètre table (TVP) avec des exemples de données et appelle la procédure pour insérer une commande client avec cinq éléments de ligne.
L’outil ostress est utilisé pour exécuter les appels de procédures stockées en parallèle, afin de simuler l’insertion simultanée de commandes par les clients.
Réinitialisez la démonstration après chaque exécution contrainte en exécutant Demo.usp_DemoReset. Cette procédure supprime les lignes des tables optimisées en mémoire, tronque les tables sur disque, et exécute un point de contrôle de base de données.
Le script suivant est exécuté simultanément pour simuler une charge de travail de traitement des commandes :
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
Avec ce script, chaque exemple de commande construite est inséré 20 fois, via 20 procédures stockées exécutées dans une boucle WHILE. La boucle est utilisée pour tenir compte du fait que la base de données est utilisée pour construire l'exemple de commande. Dans les environnements de production classiques, l’application de niveau intermédiaire construit l’ordre de vente à insérer.
Le script précédent insère des commandes commerciales dans des tables mémoire optimisées. Le script pour insérer des commandes de vente dans des tables basées sur disque est dérivé en remplaçant les deux occurrences de _inmem par _ondisk.
Nous utilisons l’outil ostress pour exécuter les scripts à l’aide de plusieurs connexions simultanées. Nous utilisons le paramètre -n pour contrôler le nombre de connexions et le paramètre r pour contrôler le nombre de fois où le script est exécuté sur chaque connexion.
Exécuter la charge de travail
Pour tester à l'échelle, nous insérons 10 millions de commandes, à l'aide de 100 connexions. Ce test peut être exécuté aisément sur un serveur de petite taille (par exemple 8 noyaux physiques et 16 noyaux logiques) et un stockage SSD de base pour le journal. Si le test ne fonctionne pas correctement sur votre matériel, consultez la section Résoudre les problèmes liés aux tests en cours d’exécution lente. Si vous souhaitez réduire le niveau de contrainte pour ce test, réduisez le nombre de connexions en modifiant le paramètre -n. Par exemple, pour réduire le nombre de connexions à 40, remplacez le paramètre -n100-n40par .
Comme mesure des performances pour la charge de travail, nous utilisons le temps écoulé, tel qu'indiqué par ostress.exe, après l'exécution de la charge de travail.
Les instructions et mesures suivantes utilisent une charge de travail qui insère 10 millions de commandes. Pour obtenir des instructions pour exécuter une charge de travail réduite en insérant 1 million de commandes de vente, consultez In-Memory OLTP\readme.txt, qui fait partie de l'archive SQLServer2016Samples.zip.
Tables optimisées en mémoire
Nous commençons par exécuter la charge de travail sur des tables mémoire optimisées. La commande suivante ouvre 100 threads, chacun exécuté pour 5 000 itérations. Chaque itération insère 20 commandes dans des transactions séparées. Il y a 20 insertions par itération pour compenser le fait que la base de données est utilisée pour générer les données à insérer. Cela donne un total de 20 * 5 000 * 100 = 10 000 000 commandes insérées.
Ouvrez l’invite de commandes RML, puis exécutez la commande suivante :
Sélectionnez le bouton Copier pour copier la commande, puis collez-la dans l’invite de commandes Utilitaires 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"
Sur un serveur de test avec un nombre total de 8 noyaux physiques (16 logiques), ceci a nécessité 2 minutes et 5 secondes. Sur un second serveur de test avec 24 noyaux physiques (48 logiques), ceci a nécessité 1 minute et 0 secondes.
Observez l'utilisation de l'UC pendant que la charge de travail est exécutée, par exemple via le Gestionnaire des tâches. Vous voyez que l’utilisation du processeur est proche de 100%. Si ce n’est pas le cas, vous avez un goulot d’étranglement au niveau des E/S de journal ; voir aussi Résolution des tests exécutés lentement.
Tables sur disque
La commande suivante exécute la charge de travail sur des tables sur disque. Cette charge de travail peut prendre un certain temps pour s’exécuter, ce qui est principalement dû à la contention du verrou dans le système. Les tables optimisées en mémoire sont sans verrou et ne souffrent donc pas de ce problème.
Ouvrez l'invite de commandes RML et exécutez la commande suivante :
Sélectionnez le bouton Copier pour copier la commande, puis collez-la dans l’invite de commandes Utilitaires 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"
Sur un serveur de test avec un nombre total de 8 noyaux physiques (16 logiques), ceci a nécessité 41 minutes et 25 secondes. Sur un second serveur de test avec 24 noyaux physiques (48 logiques), ceci a nécessité 52 minutes et 16 secondes.
Le principal facteur de la différence de performances entre les tables optimisées en mémoire et les tables basées sur disque dans ce test est que lorsque vous utilisez des tables sur disque, SQL Server ne peut pas utiliser entièrement l’UC. Cela est dû à une contention de verrou : les transactions simultanées tentent d'écrire dans la même page de données ; les verrous sont utilisés pour garantir qu'une seule transaction à la fois écrit sur une page. Le moteur OLTP In-Memory est sans verrou et les lignes de données ne sont pas organisées dans les pages. Par conséquent, les transactions simultanées ne bloquent pas les insertions mutuelles, ce qui permet à SQL Server d’utiliser entièrement le CPU.
Observez l'utilisation de l'UC pendant que la charge de travail est exécutée, par exemple via le Gestionnaire des tâches. Vous voyez avec les tables sur disque que l’utilisation du processeur est loin de 100%. Dans une configuration de test avec 16 processeurs logiques, l'utilisation serait d'environ 24 %.
Vous pouvez si vous le souhaitez afficher le nombre d’attentes de verrou interne par seconde à l’aide de l’Analyseur de performances, avec le compteur de performances \SQL Server:Latches\Latch Waits/sec.
Réinitialiser la démonstration
Pour réinitialiser la démonstration, ouvrez l'invite de commandes RML et exécutez la commande suivante :
ostress.exe -S. -E -dAdventureWorks2022 -Q"EXEC Demo.usp_DemoReset"
Selon le matériel, l’exécution peut prendre quelques minutes.
Nous vous recommandons de réinitialiser après chaque exécution de démonstration. Étant donné que cette charge de travail est insérée uniquement, chaque exécution consomme plus de mémoire et, par conséquent, une réinitialisation est nécessaire pour empêcher l’épuisement de la mémoire. La quantité de mémoire consommée après une exécution est décrite dans la section Utilisation de la mémoire après avoir exécuté la charge de travail.
Résoudre les problèmes liés aux tests exécutés lentement
Les résultats des tests varient généralement avec le matériel, ainsi que le niveau de concurrence utilisé dans l'exécution du test. Quelques éléments à rechercher si les résultats ne sont pas comme prévu :
Nombre de transactions simultanées : lors de l’exécution de la charge de travail sur un seul thread, le gain de performances avec In-Memory OLTP est probablement inférieur à 2X. La contention de latch est un problème important uniquement s'il y a un niveau élevé de concurrence.
Faible nombre de cœurs disponibles pour SQL Server : cela signifie qu’il existe un faible niveau d’accès concurrentiel dans le système, car il ne peut y avoir que autant de transactions en cours d’exécution simultanée qu’il existe des cœurs disponibles pour SQL.
- Symptôme : si l'utilisation du CPU est élevée lors de l'exécution de la charge de travail sur des tables basées sur disque, cela signifie qu'il n'y a pas beaucoup de contention, indiquant un manque de concurrence.
Vitesse du lecteur de journal : si le lecteur de journal ne peut pas suivre le niveau de débit des transactions dans le système, la charge de travail devient un goulot d'étranglement pour les E/S de journal. Bien que la journalisation soit plus efficace avec l’OLTP en mémoire, si le journal des E/S est congestionné, le gain de performance potentiel est limité.
- Symptôme : si l'utilisation du processeur n'est pas proche de 100 % ou si elle est très irrégulière lorsqu'on exécute la charge de travail sur des tables optimisées pour la mémoire, il est possible qu'il y ait un goulot d'étranglement des opérations d'E/S de journalisation. Cela peut être vérifié en ouvrant le moniteur de ressource et en examinant la longueur de la file d'attente du lecteur de journalisation.
Utilisation de la mémoire et de l’espace disque dans l’exemple
Dans l’exemple suivant, nous décrivons ce qu’il faut attendre en termes d’utilisation de la mémoire et de l’espace disque pour l’exemple de base de données. Nous affichons également les résultats d’un serveur de test avec 16 cœurs logiques.
Utilisation de la mémoire pour les tables optimisées en mémoire
Utilisation générale de la base de données
La requête suivante peut être utilisée pour obtenir l’utilisation totale de mémoire pour l’OLTP en mémoire dans le système.
SELECT type,
name,
pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Instantané de la base de données juste après sa création :
| type | name | pages_MB |
|---|---|---|
| MEMORYCLERK_XTP | Par défaut | 94 |
| MEMORYCLERK_XTP | DB_ID_5 | 877 |
| MEMORYCLERK_XTP | Par défaut | 0 |
| MEMORYCLERK_XTP | Par défaut | 0 |
Les régisseurs de mémoire par défaut contiennent les structures de mémoire à l'échelle du système et sont relativement petits. Le gestionnaire de mémoire pour la base de données utilisateur, base de données avec l’ID 5 dans ce cas (le database_id peut différer dans votre instance), est d’environ 900 Mo.
Utilisation de la mémoire par table
La requête suivante peut être utilisée pour explorer l'utilisation de la mémoire des différentes tables et de leurs index :
SELECT object_name(t.object_id) AS [Table name],
memory_allocated_for_table_kb,
memory_allocated_for_indexes_kb
FROM sys.dm_db_xtp_table_memory_stats AS dms
INNER JOIN sys.tables AS t
ON dms.object_id = t.object_id
WHERE t.type = 'U';
Le tableau suivant affiche les résultats de cette requête pour une nouvelle installation de l’exemple :
| Nom de la table | memory_allocated_for_table_kb |
memory_allocated_for_indexes_kb |
|---|---|---|
SpecialOfferProduct_inmem |
64 | 3840 |
DemoSalesOrderHeaderSeed |
1984 | 5,504 |
SalesOrderDetail_inmem |
15316 | 663552 |
DemoSalesOrderDetailSeed |
64 | 10432 |
SpecialOffer_inmem |
3 | 8192 |
SalesOrderHeader_inmem |
7168 | 147456 |
Product_inmem |
124 | 12352 |
Comme vous pouvez le voir, les tables sont assez petites : SalesOrderHeader_inmem est d’environ 7 Mo et SalesOrderDetail_inmem est d’environ 15 Mo de taille.
Ce qui est frappant ici est la taille de la mémoire allouée aux index, par rapport à la taille des données de table. Cela est dû au fait que les tables de hachage dans l'exemple sont prédéfinies pour une taille de données plus importante. Les index de hachage ont une taille fixe et, par conséquent, leur taille ne croît pas avec la taille des données de la table.
Utilisation de la mémoire après avoir exécuté la charge de travail
Après avoir ajouté 10 millions de commandes de vente, l'utilisation de la mémoire globale ressemble à ceci dans la requête suivante :
SELECT type,
name,
pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Voici l'ensemble des résultats.
type |
name |
pages_MB |
|---|---|---|
| MEMORYCLERK_XTP | Par défaut | 146 |
| MEMORYCLERK_XTP | DB_ID_5 | 7374 |
| MEMORYCLERK_XTP | Par défaut | 0 |
| MEMORYCLERK_XTP | Par défaut | 0 |
Comme vous pouvez le voir, SQL Server utilise un peu moins de 8 Go pour les tables à mémoire optimisée et les index dans l’exemple de base de données.
Voici l'utilisation de la mémoire détaillée par table après l'exécution d'un exemple :
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';
Voici l'ensemble des résultats.
Table name |
memory_allocated_for_table_kb |
memory_allocated_for_indexes_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 | 5,504 |
Nous pouvons voir un total d’environ 6,5 Go de données. La taille des index sur la table SalesOrderHeader_inmem et SalesOrderDetail_inmem est identique à la taille des index avant l'insertion des commandes de vente. La taille de l’index n’a pas changé, car les deux tables utilisent des index de hachage et les index de hachage sont statiques.
Après la réinitialisation de la démonstration
La procédure stockée Demo.usp_DemoReset peut être utilisée pour réinitialiser la démonstration. Il supprime les données dans les tables SalesOrderHeader_inmem et SalesOrderDetail_inmem, et réinitialise les données à partir des tables d'origine SalesOrderHeader et SalesOrderDetail.
Maintenant, même si les lignes des tables ont été supprimées, cela ne signifie pas que la mémoire est récupérée immédiatement. SQL Server récupère la mémoire des lignes supprimées dans les tables optimisées en mémoire en arrière-plan, si nécessaire. Vous voyez que immédiatement après la réinitialisation de démonstration, sans charge de travail transactionnelle sur le système, la mémoire des lignes supprimées n’est pas encore récupérée :
SELECT type,
name,
pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Voici l'ensemble des résultats.
type |
name |
pages_MB |
|---|---|---|
| MEMORYCLERK_XTP | Par défaut | 2261 |
| MEMORYCLERK_XTP | DB_ID_5 | 7396 |
| MEMORYCLERK_XTP | Par défaut | 0 |
| MEMORYCLERK_XTP | Par défaut | 0 |
Cela est attendu : la mémoire est récupérée lorsque la charge de travail transactionnelle est en cours d’exécution.
Si vous démarrez une deuxième exécution de la charge de travail de démonstration, vous voyez que l’utilisation de la mémoire diminue initialement, car les lignes précédemment supprimées sont nettoyées. À un moment donné, la taille de la mémoire augmente à nouveau jusqu’à ce que la charge de travail se termine. Après avoir inséré 10 millions de lignes après la réinitialisation de la démonstration, l’utilisation de la mémoire est très similaire à l’utilisation après la première exécution. Par exemple :
SELECT type,
name,
pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Voici l'ensemble des résultats.
type |
name |
pages_MB |
|---|---|---|
| MEMORYCLERK_XTP | Par défaut | 1,863 |
| MEMORYCLERK_XTP | DB_ID_5 | 7390 |
| MEMORYCLERK_XTP | Par défaut | 0 |
| MEMORYCLERK_XTP | Par défaut | 0 |
Utilisation du disque pour les tables optimisées en mémoire
La taille globale sur disque des fichiers de point de contrôle d'une base de données à un moment donné peut être récupérée à l'aide de la requête :
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';
InitialState
Lorsque l’exemple de groupe de fichiers et d’exemples de tables mémoire optimisées est créé initialement, plusieurs fichiers de point de contrôle sont précréés et le système commence à remplir les fichiers : le nombre de fichiers de point de contrôle précréés dépend du nombre de processeurs logiques dans le système. Comme l’exemple est initialement très petit, les fichiers précréés sont principalement vides après la création initiale.
Le code suivant montre la taille initiale sur disque de l’exemple sur une machine dotée de 16 processeurs logiques :
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';
Voici l'ensemble des résultats.
| Taille sur disque en Mo |
|---|
| 2312 |
Comme vous pouvez le constater, il existe une différence importante entre la taille sur disque des fichiers de point de contrôle, qui est de 2,3 Go et la taille réelle des données, qui est plus proche de 30 Mo.
Pour analyser de plus près la raison de l'utilisation de l'espace disque, vous pouvez utiliser la requête suivante, La taille du disque retournée par cette requête est approximative pour les fichiers ayant l'état 5 (REQUIRED FOR BACKUP/HA), 6 (IN TRANSITION TO TOMBSTONE) ou 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;
Pour l’état initial de l’exemple, le résultat ressemble au tableau suivant pour un serveur avec 16 processeurs logiques :
| state_desc | file_type_desc | count | taille sur disque en Mo |
|---|---|---|---|
| PRECREATED | DONNÉES | 16 | 2 048 |
| PRECREATED | DELTA | 16 | 128 |
| EN CONSTRUCTION | DONNÉES | 1 | 128 |
| EN CONSTRUCTION | DELTA | 1 | 8 |
Comme vous pouvez le voir, la majeure partie de l'espace est utilisé par les fichiers de données et delta précréés. SQL Server a précrée une paire de fichiers (données, delta) pour chaque processeur logique. En outre, les fichiers de données sont prédimensionnés à 128 Mo et les fichiers delta à 8 Mo, afin de rendre l’insertion de données dans ces fichiers plus efficace.
Les données réelles dans les tables optimisées en mémoire se trouvent dans un seul fichier de données.
Après l'exécution de la charge de travail
Après une seule exécution de test qui insère 10 millions de commandes, la taille totale sur disque ressemble à ce qui suit (pour un serveur de test avec 16 noyaux) :
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';
Voici l'ensemble des résultats.
| Taille sur disque en Mo |
|---|
| 8828 |
La taille sur disque est proche de 9 Go, ce qui est proche de la taille en mémoire des données.
Analysons plus en détail les tailles des fichiers de point de contrôle entre les différents états :
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;
Voici l'ensemble des résultats.
state_desc |
file_type_desc |
count |
on-disk size MB |
|---|---|---|---|
| PRECREATED | DONNÉES | 16 | 2 048 |
| PRECREATED | DELTA | 16 | 128 |
| EN CONSTRUCTION | DONNÉES | 1 | 128 |
| EN CONSTRUCTION | DELTA | 1 | 8 |
Nous avons encore 16 paires de fichiers précréés, prêtes à être utilisées car les points de contrôle sont finalisés.
Il existe une paire en construction, qui est utilisée jusqu’à ce que le point de contrôle actuel soit fermé. Avec les fichiers de point de contrôle actifs, cela donne environ 6,5 Go d’utilisation du disque pour 6,5 Go de données en mémoire. Rappelez-vous que les index ne sont pas conservés sur le disque, et par conséquent, la taille globale sur le disque est inférieure à la taille en mémoire dans ce cas.
Après la réinitialisation de la démonstration
Après la réinitialisation de la démonstration, l’espace disque n’est pas récupéré immédiatement s’il n’existe aucune charge de travail transactionnelle sur le système, et il n’y a pas de points de contrôle de base de données. Pour que les fichiers de point de contrôle soient déplacés à travers différentes étapes et ultimement supprimés, plusieurs points de contrôle et événements de troncation de journal doivent se produire pour initier la fusion des fichiers de point de contrôle ainsi que la collecte des ordures. Celles-ci se produisent automatiquement si vous avez une charge de travail transactionnelle dans le système (et effectuez des sauvegardes de journaux régulières, au cas où vous utilisez le modèle de récupération COMPLÈTE), mais pas lorsque le système est inactif, comme dans un scénario de démonstration.
Dans l’exemple, après la réinitialisation de démonstration, vous pouvez voir quelque chose comme suit :
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';
Voici l'ensemble des résultats.
| Taille sur disque en Mo |
|---|
| 11839 |
À presque 12 Go, cela dépasse nettement les 9 Go que nous avions avant la réinitialisation de la démonstration. Cela est dû au fait que certaines fusions de fichiers de point de contrôle ont été démarrées, mais que certaines cibles de fusion n’ont pas encore été installées et que certains fichiers sources de fusion n’ont pas encore été nettoyés, comme indiqué dans l’exemple suivant :
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;
Voici l'ensemble des résultats.
state_desc |
file_type_desc |
count |
on-disk size MB |
|---|---|---|---|
| PRECREATED | DONNÉES | 16 | 2 048 |
| PRECREATED | DELTA | 16 | 128 |
| ACTIVE | DONNÉES | 38 | 5152 |
| ACTIVE | DELTA | 38 | 1331 |
| CIBLE DE FUSION | DONNÉES | 7 | 896 |
| CIBLE DE FUSION | DELTA | 7 | 56 |
| SOURCE FUSIONNÉE | DONNÉES | 13 | 1,772 |
| SOURCE FUSIONNÉE | DELTA | 13 | 4:55 |
Les cibles de fusion sont installées et la source fusionnée est nettoyée au fur et à mesure que l'activité transactionnelle s'exécute dans le système.
Après une deuxième exécution de la charge de travail de démonstration, en insérant 10 millions de commandes après la réinitialisation de la démonstration, vous voyez que les fichiers construits lors de la première exécution de la charge de travail ont été nettoyés. Si vous exécutez la requête précédente plusieurs fois pendant l’exécution de la charge de travail, vous pouvez voir les fichiers de point de contrôle passer par les différentes étapes.
Après la deuxième exécution de la charge de travail, insérez 10 millions de commandes de vente. Vous verrez que l’utilisation du disque est très similaire, mais pas nécessairement identique, à celle après la première exécution, car le système est par nature dynamique. Par exemple :
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;
Voici l'ensemble des résultats.
state_desc |
file_type_desc |
count |
on-disk size MB |
|---|---|---|---|
| PRECREATED | DONNÉES | 16 | 2 048 |
| PRECREATED | DELTA | 16 | 128 |
| EN CONSTRUCTION | DONNÉES | 2 | 268 |
| EN CONSTRUCTION | DELTA | 2 | 16 |
| ACTIVE | DONNÉES | 41 | 5608 |
| ACTIVE | DELTA | 41 | 328 |
Dans ce cas, il existe deux paires de fichiers de point de contrôle dans l’état UNDER CONSTRUCTION , ce qui signifie que plusieurs paires de fichiers ont été déplacées vers l’état UNDER CONSTRUCTION , probablement en raison du niveau élevé de concurrence dans la charge de travail. Plusieurs threads simultanés ont requis une nouvelle paire de fichiers en même temps, et ont donc déplacé une paire vers PRECREATEDUNDER CONSTRUCTION.