web-dev-qa-db-fra.com

Quelles sont les principales différences entre OPTION (OPTIMIZE FOR UNKNOWN) et OPTION (RECOMPILE)?

Je rencontre les problèmes classiques Parameter Sniffing dans SQL Server 2012. D'après certaines recherches, j'ai trouvé plusieurs options autour de ce problème. Les deux options dont j'ai besoin pour comprendre la différence sont OPTION(OPTIMIZE FOR UNKNOWN) vs OPTION(RECOMPILE).

J'hésite à utiliser OPTION(RECOMPILE) à la fin de mes requêtes présentant ce problème, car le serveur sera obligé de générer un nouveau plan d'exécution à chaque fois. Si j'appelle souvent cette requête, cela augmentera le CPU de cette machine. 

Pour utiliser la meilleure solution disponible, quelles sont les différences réelles entre les deux options? 

OPTION(OPTIMIZE FOR UNKNOWN) réutilisera-t-il le cache au lieu de recompiler chaque fois?

8
Mike A

OPTION(OPTIMIZE FOR UNKNOWN) réutilisera-t-il le cache au lieu de recompiler chaque fois?

Oui, il sera.


Il existe deux différences principales entre OPTION(OPTIMIZE FOR UNKNOWN) et OPTION(RECOMPILE), comme l'indique cette citation de MSDN :

OPTIMIZE FOR UNKNOWN 

Ordonne à l'optimiseur de requêtes d'utiliser statistique. données au lieu des valeurs initiales pour toutes les variables locales lorsque le La requête est compilée et optimisée, y compris les paramètres créés avec paramétrage forcé.

RECOMPILE

Ordonne au moteur de base de données SQL Server de rejeter le plan généré pour la requête après son exécution, forçant l'optimiseur de requête recompiler un plan de requête la prochaine fois que la même requête est exécutée . Sans spécifier RECOMPILE, le moteur de base de données met en cache les plans de requête et les réutilise. Lors de la compilation des plans de requête, l'indicateur de requête RECOMPILE utilise les valeurs actuelles de toutes les variables locales dans la requête et, si la requête est à l'intérieur d'une procédure stockée, les valeurs actuelles sont transmises à tous les paramètres.

Donc, les deux différences principales sont:

  1. Mise en cache (ou non) du plan de requête.

Généralement, le plan de requête généré est mis en cache et réutilisé. OPTIMIZE FOR UNKNOWN n'affecte pas cette fonctionnalité du moteur. RECOMPILE supprime cette fonctionnalité et demande au moteur de rejeter le plan et de ne pas le placer dans le cache. 

  1. Utilisation (ou non) des valeurs de paramètre réelles lors de la génération du plan.

Généralement, l’optimiseur "détecte" les valeurs des paramètres et les utilise lors de la génération du plan. OPTIMIZE FOR UNKNOWN supprime cette fonctionnalité et demande au moteur de traiter tous les paramètres comme si leurs valeurs étaient inconnues. Optimizer possède des règles et des méthodes heuristiques intégrées pour utiliser les statistiques disponibles pour différents critères de filtrage. Voir Optimiser pour… médiocre? pour plus de détails. Normalement, le sniffing de paramètre est utilisé lors de la première exécution de la requête/procédure stockée et utilise les valeurs des paramètres lors de la première exécution. Le plan généré est mis en cache et peut être réutilisé ultérieurement. 

Une chose non évidente à retenir ici est que dans les deux cas (normal sans indication de requête et avec l'indication OPTIMIZE FOR UNKNOWN), le plan généré doit être valide et produire un résultat correct pour la valeur de paramètre any possible. Il est adapté aux valeurs détectées qui ont été utilisées lors de la première analyse dans le cas normal/sans indice. il n'est pas adapté à une valeur spécifique dans la casse OPTIMIZE FOR UNKNOWN, mais il reste valable si le paramètre est modifié ultérieurement, de quelque manière que ce soit. 

Ceci est important et empêche l’optimiseur d’effectuer certaines transformations et simplifications du plan.

OPTION(RECOMPILE) permet à l’optimiseur d’aligner les valeurs réelles des paramètres lors de chaque exécution et l’optimiseur utilise les valeurs réelles des paramètres pour générer un meilleur plan. Il n'est pas à craindre que le plan généré ne fonctionne pas avec une autre valeur de paramètre, car le plan ne sera ni mis en cache ni réutilisé.

Cet effet est principalement visible pour les Conditions de recherche dynamique requêtes. Par exemple:

SELECT ...
FROM T
WHERE
    (@ParamSomeID = 0)
    OR
    (
        @ParamSomeID = -1
        AND
        T.SomeID NOT IN
        (
            SELECT OtherTable.SomeID
            FROM OtherTable
        )
    )
    OR
    (
        T.SomeID IN
        (
            SELECT OtherTable.SomeID
            FROM OtherTable
            WHERE OtherTable.SomeID = @ParamSomeID
        )
    )
OPTION(RECOMPILE)

Si @ParamSomeID est 0, l’optimiseur traitera la requête comme si elle n’avait aucune clause WHERE. Le plan ne mentionnerait pas OtherTable du tout. 

Si @ParamSomeID est -1, le plan joignera T à OtherTable à l'aide de Left Anti Semi Join et analysera l'intégralité de OtherTable

Si @ParamSomeID est, disons, 5, le plan fera une recherche d'index dans un index unique sur OtherTable et lira une seule ligne de OtherTable.

Sans OPTION(RECOMPILE), ce type de simplification et de transformation n’aurait pas lieu. 

Une autre raison d'utiliser OPTION(RECOMPILE) est lorsque votre distribution de données est très asymétrique. Par exemple, vous avez une table avec 1 million de lignes. Une colonne a la valeur 0 sur 990K lignes et les valeurs de 1 à 10 sur 1K. Les requêtes filtrant sur cette colonne doivent avoir des plans différents en fonction de la valeur réelle du filtre.

Dans les deux exemples ci-dessus, OPTIMIZE FOR UNKNOWN générerait un plan médiocre.

8
Vladimir Baranov

OPTION (OPTIMIZE FOR UNKNOWN) va-t-il réutiliser le cache au lieu de recompiler à chaque fois?

Oui. Optimiser pour inconnu influencera la façon dont le plan est généré (c’est-à-dire l’empêche explicitement de renifler des paramètres et de le comparer avec l’histogramme de données de colonne), mais une fois généré, le plan reste en cache et est réutilisé.

OPTION(RECOMPILE) force la recompilation à chaque exécution et constitue une approche plutôt lourde. Cela n'a de sens que dans un environnement analytique DW/BI où chaque requête peut être différente, complexe et probablement avec un temps d'exécution important.

Vous avez également d'autres options à votre disposition:

Tous deux vous permettent d'obtenir le même effet que dans votre message, mais de manière non invasive (pas de modification du code de l'application/de la requête).

7
Remus Rusanu

J'ai utilisé les deux. OPTION(OPTIMIZE FOR UNKNOWN) a été utilisé pour une procédure stockée de recherche intensive qui prenait une variété de paramètres. Certaines conditions, inconnues de moi (statistiques et autres), risquaient de gêner l'optimisation, la requête était banale, mais elle causerait de graves retards (et même un délai d'attente). OPTION(OPTIMIZE FOR UNKNOWN) a résolu ce problème mais n'était pas idéal.

La même procédure de recherche intensive présenterait des problèmes intermittents, ce qui signifie que la recherche expirerait au bout de quelques mois. La solution immédiate consisterait à appeler sp_recompile, ce qui revient à ajouter une clause OPTION(RECOMPILE) à la procédure stockée.

Les entrailles de la procédure stockée ont propulsé un "résultat en cours de frappe" solution dans lequel toutes les trois frappes au clavier déclencheraient une recherche dans la base de données et les résultats figureraient dans une liste déroulante. 

En fin de compte, j'ai supprimé la OPTION(OPTIMIZE FOR UNKNOWN) et ajouté simplement un EXEC sp_recompile<sp> à mon travail de maintenance nocturne, ce qui a résolu tous les problèmes.

1
Ross Bush