Pour autant que je sache, vous pouvez optimiser un insert en vrac d'une manière très similaire à celle d'un insert régulier. En règle générale, un plan de requête pour une insertion simple n'est pas très informatif, alors ne vous inquiétez pas de ne pas avoir le plan. Je vais passer en revue quelques façons d'optimiser un insert, mais la plupart d'entre elles ne s'appliquent probablement pas à l'insert que vous avez spécifié dans la question. Cependant, ils pourraient être utiles si à l'avenir vous deviez charger de plus grandes quantités de données.
1. Insérer des données dans l'ordre des clés de clustering
SQL Server trie souvent les données avant de les insérer dans une table avec un index cluster. Pour certaines tables et applications, vous pouvez améliorer les performances en triant les données dans le fichier plat et en indiquant à SQL Server que les données sont triées via l' ORDER
argument de BULK INSERT
:
COMMANDE ({colonne [ASC | DESC]} [, ... n])
Spécifie comment les données du fichier de données sont triées. Les performances d'importation en masse sont améliorées si les données importées sont triées en fonction de l'index cluster sur la table, le cas échéant.
Puisque vous utilisez une IDENTITY
colonne comme clé en cluster, vous n'avez pas à vous en préoccuper.
2. Utilisez TABLOCK
si possible
Si vous êtes assuré d'avoir une seule session insérant des données dans votre table, vous pouvez spécifier l' TABLOCK
argument pour BULK INSERT
. Cela peut réduire les conflits de verrous et conduire à une journalisation minimale dans certains scénarios. Cependant, vous insérez dans une table avec un index cluster qui contient déjà des données afin que vous n'obteniez pas une journalisation minimale sans l'indicateur de trace 610 qui est mentionné plus loin dans cette réponse.
Si ce TABLOCK
n'est pas possible, parce que vous ne pouvez pas changer le code , tout espoir n'est pas perdu. Pensez à utiliser sp_table_option
:
EXEC [sys].[sp_tableoption]
@TableNamePattern = N'dbo.BulkLoadTable' ,
@OptionName = 'table lock on bulk load' ,
@OptionValue = 'ON'
Une autre option consiste à activer l' indicateur de trace 715 .
3. Utilisez une taille de lot appropriée
Parfois, vous pourrez régler les insertions en modifiant la taille du lot.
ROWS_PER_BATCH = row_per_batch
Indique le nombre approximatif de lignes de données dans le fichier de données.
Par défaut, toutes les données du fichier de données sont envoyées au serveur en tant que transaction unique et le nombre de lignes du lot est inconnu de l'optimiseur de requêtes. Si vous spécifiez ROWS_PER_BATCH (avec une valeur> 0), le serveur utilise cette valeur pour optimiser l'opération d'importation en bloc. La valeur spécifiée pour ROWS_PER_BATCH doit être approximativement la même que le nombre réel de lignes. Pour plus d'informations sur les considérations de performances, voir «Remarques», plus loin dans cette rubrique.
Voici la citation de plus tard dans l'article:
Si le nombre de pages à vider dans un même lot dépasse un seuil interne, une analyse complète du pool de mémoire tampon peut se produire pour identifier les pages à vider lorsque le lot est validé. Cette analyse complète peut nuire aux performances d'importation en masse. Un cas probable de dépassement du seuil interne se produit lorsqu'un grand pool de mémoire tampon est combiné avec un sous-système d'E / S lent. Pour éviter les débordements de tampon sur les grandes machines, n'utilisez pas l'indication TABLOCK (qui supprimera les optimisations en bloc) ou utilisez une taille de lot plus petite (qui préserve les optimisations en bloc).
Étant donné que les ordinateurs varient, nous vous recommandons de tester différentes tailles de lots avec votre chargement de données pour savoir ce qui vous convient le mieux.
Personnellement, je voudrais simplement insérer les 695 lignes en un seul lot. Cependant, le réglage de la taille du lot peut faire une grande différence lors de l'insertion de nombreuses données.
4. Assurez-vous que vous avez besoin de la IDENTITY
colonne
Je ne sais rien de votre modèle de données ou de vos exigences, mais ne tombez pas dans le piège d'ajouter une IDENTITY
colonne à chaque table. Aaron Bertrand a un article à ce sujet appelé Bad habitudes to kick: mettre une colonne IDENTITY sur chaque table . Pour être clair, je ne dis pas que vous devez supprimer la IDENTITY
colonne de ce tableau. Cependant, si vous déterminez que la IDENTITY
colonne n'est pas nécessaire et que vous la supprimez, cela pourrait améliorer les performances d'insertion.
5. Désactiver les index ou les contraintes
Si vous chargez une grande quantité de données dans une table par rapport à ce que vous avez déjà, il peut être plus rapide de désactiver les index ou les contraintes avant le chargement et de les activer après le chargement. Pour de grandes quantités de données, il est généralement plus inefficace pour SQL Server de créer un index en une seule fois plutôt que lorsque les données sont chargées dans la table. Il semble que vous ayez inséré 695 lignes dans un tableau avec 11500 lignes, donc je ne recommanderais pas cette technique.
6. Considérez TF 610
L'indicateur de trace 610 permet une journalisation minimale dans certains scénarios supplémentaires. Pour votre table avec une IDENTITY
clé en cluster, vous obtiendrez une journalisation minimale pour toutes les nouvelles pages de données tant que votre modèle de récupération est simple ou enregistré en bloc. Je pense que cette fonctionnalité n'est pas activée par défaut car elle peut dégrader les performances sur certains systèmes. Vous devrez tester soigneusement avant d'activer cet indicateur de trace. La référence Microsoft recommandée semble toujours être le Guide de performances de chargement des données
Impact des E / S de la journalisation minimale sous l'indicateur de trace 610
Lorsque vous validez une transaction de chargement en bloc qui a été journalisée de manière minimale, toutes les pages chargées doivent être vidées sur le disque avant la fin de la validation. Toutes les pages vidées non capturées par une opération de point de contrôle antérieure peuvent créer beaucoup d'E / S aléatoires. Comparez cela à une opération entièrement journalisée, qui crée à la place des E / S séquentielles sur les écritures de journal et ne nécessite pas de vidage des pages chargées sur le disque au moment de la validation.
Si votre scénario de chargement consiste en de petites opérations d'insertion sur des btrees qui ne franchissent pas les limites des points de contrôle et que vous avez un système d'E / S lent, l'utilisation d'une journalisation minimale peut en fait ralentir les vitesses d'insertion.
Pour autant que je sache, cela n'a rien à voir avec l'indicateur de trace 610, mais plutôt avec une journalisation minimale elle-même. Je crois que la citation précédente sur le ROWS_PER_BATCH
réglage aboutissait à ce même concept.
En conclusion, vous ne pouvez probablement pas faire grand chose pour régler votre BULK INSERT
. Je ne serais pas préoccupé par le nombre de lectures que vous avez observé avec votre encart. SQL Server signale les lectures chaque fois que vous insérez des données. Considérez ce qui suit très simplement INSERT
:
DROP TABLE IF EXISTS X_TABLE;
CREATE TABLE X_TABLE (
VAL VARCHAR(1000) NOT NULL
);
SET STATISTICS IO, TIME ON;
INSERT INTO X_TABLE WITH (TABLOCK)
SELECT REPLICATE('Z', 1000)
FROM dbo.GetNums(10000); -- generate 10000 rows
Sortie de SET STATISTICS IO, TIME ON
:
Tableau 'X_TABLE'. Nombre de balayages 0, lectures logiques 11428
J'ai 11428 rapports lus mais ce ne sont pas des informations exploitables. Parfois, le nombre de lectures signalées peut être réduit par une journalisation minimale, mais bien sûr, la différence ne peut pas être directement traduite en un gain de performances.