Y a-t-il un avantage plausible pour l'itérateur de bobine dans le premier plan?
Cela dépend de ce que vous considérez comme «plausible», mais la réponse selon le modèle de coût est oui. Bien sûr, cela est vrai, car l'optimiseur choisit toujours le plan le moins cher qu'il trouve.
La vraie question est de savoir pourquoi le modèle de coût considère le plan avec la bobine tellement moins cher que le plan sans. Considérez les plans estimés créés pour une nouvelle table (à partir de votre script) avant d'ajouter des lignes au magasin delta:
DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE);
Le coût estimé de ce plan est de 771 734 unités :
Le coût est presque entièrement associé à la suppression d'index en cluster, car les suppressions devraient entraîner de nombreuses E / S aléatoires. C'est juste la logique générique qui s'applique à toutes les modifications de données. Par exemple, un ensemble non ordonné de modifications d'un index b-tree est supposé entraîner des E / S largement aléatoires, avec un coût d'E / S élevé associé.
Les plans de modification des données peuvent comporter un tri pour présenter les lignes dans un ordre qui favorisera l'accès séquentiel, pour exactement ces raisons de coût. L'impact est exacerbé dans ce cas car la table est partitionnée. Très partitionné, en fait; votre script en crée 15 000. Les mises à jour aléatoires d'une table très partitionnée sont particulièrement coûteuses, car le prix du basculement de partitions (ensembles de lignes) en milieu de flux est également très coûteux.
Le dernier facteur majeur à considérer est que la simple requête de mise à jour ci-dessus (où «mise à jour» signifie toute opération de changement de données, y compris une suppression) remplit les conditions d'une optimisation appelée «partage d'ensemble de lignes», où le même ensemble de lignes interne est utilisé à la fois pour l'analyse et mise à jour de la table. Le plan d'exécution montre toujours deux opérateurs distincts, mais néanmoins, un seul ensemble de lignes est utilisé.
Je mentionne cela parce que pouvoir appliquer cette optimisation signifie que l'optimiseur prend un chemin de code qui ne tient tout simplement pas compte des avantages potentiels d' un tri explicite pour réduire le coût des E / S aléatoires. Lorsque la table est un arbre B, cela a du sens, car la structure est intrinsèquement ordonnée, donc le partage de l'ensemble de lignes fournit automatiquement tous les avantages potentiels.
La conséquence importante est que la logique de calcul des coûts pour l'opérateur de mise à jour ne prend pas en compte cet avantage de classement (promotion d'E / S séquentielles ou d'autres optimisations) lorsque l'objet sous-jacent est le stockage de colonnes. Cela est dû au fait que les modifications du magasin de colonnes ne sont pas effectuées sur place; ils utilisent un magasin delta. Le modèle de coût reflète donc une différence entre les mises à jour des ensembles de lignes partagées sur les arbres b et les magasins de colonnes.
Néanmoins, dans le cas particulier d'un magasin de colonnes partitionné (très!), Il pourrait toujours être avantageux de conserver l'ordre dans la mesure où effectuer toutes les mises à jour d'une partition avant de passer à la suivante pourrait toujours être avantageux du point de vue des E / S. .
La logique de coût standard est réutilisée pour les magasins de colonnes ici, donc un plan qui préserve l'ordre des partitions (mais pas dans chaque partition) est moins cher. Nous pouvons le voir sur la requête de test en utilisant l'indicateur de trace non documenté 2332 pour exiger une entrée triée vers l'opérateur de mise à jour. Cela définit la DMLRequestSort
propriété sur true lors de la mise à jour et force l'optimiseur à produire un plan qui fournit toutes les lignes pour une partition avant de passer à la suivante:
DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332);
Le coût estimé de ce plan est très inférieur, à 52,5174 unités:
Cette réduction des coûts est entièrement due au coût d'E / S estimé inférieur lors de la mise à jour. Le spool introduit n'effectue aucune fonction utile, sauf qu'il peut garantir la sortie dans l'ordre des partitions, comme requis par la mise à jour avec DMLRequestSort = true
(l'analyse en série d'un index de stockage de colonnes ne peut pas fournir cette garantie). Le coût de la bobine elle-même est considéré comme relativement faible, en particulier par rapport à la réduction (probablement irréaliste) des coûts lors de la mise à jour.
La décision de demander ou non une entrée ordonnée à l'opérateur de mise à jour est prise très tôt dans l'optimisation des requêtes. Les heuristiques utilisées dans cette décision n'ont jamais été documentées, mais peuvent être déterminées par essais et erreurs. Il semble que la taille des magasins delta soit un élément de cette décision. Une fois fait, le choix est permanent pour la compilation de la requête. Aucun USE PLAN
indice ne réussira: la cible du plan a soit ordonné l'entrée de la mise à jour, soit elle ne l'a pas fait.
Il existe un autre moyen d'obtenir un plan à faible coût pour cette requête sans limiter artificiellement l'estimation de cardinalité. Une estimation suffisamment basse pour éviter le spool entraînera probablement une erreur sur DMLRequestSort, entraînant un coût de plan estimé très élevé en raison des E / S aléatoires attendues. Une alternative consiste à utiliser l'indicateur de trace 8649 (plan parallèle) en conjonction avec 2332 (DMLRequestSort = true):
DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332, QUERYTRACEON 8649);
Il en résulte un plan qui utilise une analyse parallèle en mode batch par partition et un échange Gather Streams préservant l'ordre (fusion):
Selon l'efficacité au moment de l'exécution de la commande de partition sur votre matériel, cela peut fonctionner au mieux des trois. Cela dit, les grandes modifications ne sont pas une bonne idée sur le magasin de colonnes, donc l'idée de changement de partition est presque certainement meilleure. Si vous pouvez faire face aux longs temps de compilation et aux choix de plans originaux souvent vus avec les objets partitionnés - en particulier lorsque le nombre de partitions est important.
La combinaison de nombreuses fonctionnalités relativement nouvelles, en particulier près de leurs limites, est un excellent moyen d'obtenir de mauvais plans d'exécution. La profondeur de la prise en charge de l'optimiseur a tendance à s'améliorer avec le temps, mais l'utilisation de 15 000 partitions de magasin de colonnes signifie toujours que vous vivez à une époque intéressante.
OPTION (QUERYRULEOFF EnforceHPandAccCard)
la bobine disparaît. Je suppose que HP pourrait être "Halloween Protection". Cependant, alors essayer d'utiliser ce plan avec unUSE PLAN
indice échoue (tout comme essayer d'utiliser le plan à partir de laOPTIMIZE FOR
solution de contournement aussi)