Quels facteurs de coût entrent dans l'optimiseur en choisissant différents types de bobines?

15

Spoolum

Dans SQL Server, il existe plusieurs types de spools. Les deux qui m'intéressent sont les bobines de table et les bobines d'index , en dehors des requêtes de modification .

Les requêtes en lecture seule, en particulier sur le côté interne d'une jointure de boucles imbriquées, peuvent utiliser un spouleur de table ou d'index pour potentiellement réduire les E / S et améliorer les performances des requêtes. Ces bobines peuvent être désireuses ou paresseuses . Comme toi et moi.

Mes questions sont:

  • Quels facteurs entrent dans le choix entre Table et Index Spool
  • Quels facteurs entrent dans le choix entre Eager et Lazy Spools
Erik Darling
la source

Réponses:

12

C'est un peu large mais je pense que je comprends la vraie question et je répondrai en conséquence. Je vais juste parler de la table contre la bobine d'index. Je ne pense pas qu'il soit tout à fait correct de considérer qu'il s'agit d'un choix entre les spoules de table et d'index. Comme vous le savez, il est possible dans une seule sous-arborescence d'obtenir une bobine d'index, une bobine de table ou les deux une bobine d'index et une bobine de table. Je pense qu'il est généralement correct de dire que vous obtenez une bobine d'indexation dans les conditions suivantes:

  1. L'optimiseur de requêtes a une raison de transformer une jointure en application
  2. L'optimiseur de requêtes effectue réellement la transformation vers l'application
  3. L'optimiseur de requêtes utilise la règle pour ajouter une bobine d'index (au minimum, la bobine d'index doit être sûre à utiliser)
  4. Le plan avec la bobine d'index est sélectionné

Vous pouvez voir la plupart de ces derniers avec des démos simples. Commencez par créer une paire de tas:

DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_901;
CREATE TABLE dbo.X_10000_VARCHAR_901 (ID VARCHAR(901) NOT NULL);

INSERT INTO dbo.X_10000_VARCHAR_901 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;


DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_800;
CREATE TABLE dbo.X_10000_VARCHAR_800 (ID VARCHAR(800) NOT NULL);

INSERT INTO dbo.X_10000_VARCHAR_800 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

Pour la première requête, il n'y a rien à chercher sur:

SELECT *
FROM dbo.X_10000_VARCHAR_901 a
CROSS JOIN dbo.X_10000_VARCHAR_901 b
OPTION (MAXDOP 1);

Il n'y a donc aucune raison pour que l'optimiseur transforme la jointure en application. Vous vous retrouvez avec une bobine de table pour des raisons de coût. Cette requête échoue donc au premier test.

entrez la description de l'image ici

Pour la requête suivante, il est juste de s'attendre à ce que l'optimiseur ait une raison d'envisager une application:

SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID = b.ID 
OPTION (LOOP JOIN, MAXDOP 1);

Mais ce n'est pas censé être:

entrez la description de l'image ici

Cette requête échoue au deuxième test. Une explication complète est ici . Citant la partie la plus pertinente:

L'optimiseur n'envisage pas de créer un index à la volée pour activer une application; la séquence des événements est plutôt l'inverse: transformer pour appliquer car un bon index existe.

Je peux réécrire la requête pour encourager l'optimiseur à envisager d'appliquer:

SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (MAXDOP 1);

Mais il n'y a toujours pas de bobine d'index:

entrez la description de l'image ici

Cette requête échoue au troisième test. Dans SQL Server 2014, il y avait une limite de longueur de clé d'index de 900 octets. Cela a été étendu dans SQL Server 2016, mais uniquement pour les index non cluster. L'index pour un spool est un index clusterisé donc la limite reste à 900 octets . Dans tous les cas, la règle de spoule d'index ne peut pas être appliquée car elle pourrait entraîner une erreur lors de l'exécution de la requête.

La réduction de la longueur du type de données à 800 fournit enfin un plan avec une bobine d'index:

entrez la description de l'image ici

Le plan de bobine d'index, sans surprise, coûte beaucoup moins cher qu'un plan sans bobine: 89,7603 unités contre 598,832 unités. Vous pouvez voir la différence avec l' QUERYRULEOFF BuildSpoolindice de requête non documentée :

entrez la description de l'image ici

Ce n'est pas une réponse complète, mais j'espère que c'est une partie de ce que vous cherchiez.

Joe Obbish
la source