Je vais répondre à vos questions dans un ordre différent de celui que vous leur avez posé.
4. Est-ce le symptôme d'un problème plus important?
Le nouvel estimateur de cardinalité dans SQL Server 2016 pourrait contribuer au problème. SQL Server 2012 utilise l'héritage CE et vous n'avez rencontré aucun problème avec cette version. Le nouvel estimateur de cardinalité émet différentes hypothèses sur vos données et peut générer différents plans de requête pour le même SQL. Vous pouvez rencontrer de meilleures performances pour certaines requêtes avec le CE hérité en fonction de votre requête et de vos données. Par conséquent, certaines parties de votre modèle de données peuvent ne pas correspondre le mieux au nouveau CE. Ce n'est pas grave, mais vous devrez peut-être contourner le nouveau CE pour l'instant.
Je serais également préoccupé par les performances de requête incohérentes, même avec les mises à jour quotidiennes des statistiques. Une chose importante à noter est que la collecte de statistiques sur toutes les tables effacera efficacement tous les plans de requête du cache, de sorte que vous pourriez avoir un problème avec les statistiques ou avec le reniflage des paramètres. Il est difficile de prendre une décision sans beaucoup d'informations sur votre modèle de données, les données taux de changement, les statistiques de mise à jour des politiques, comment vous appelez votre code, etc. SQL Server 2016 offre quelques paramètres de niveau de base de données pour la détection des paramètres qui pourrait être utile , mais cela pourrait affecter l'ensemble de votre application au lieu d'une seule requête problématique.
Je vais jeter un exemple de scénario qui pourrait conduire à ce comportement. Tu as dit:
Certains utilisateurs peuvent avoir 1 enregistrement d'autorisation, certains jusqu'à 20 000.
Supposons que vous collectiez des statistiques sur toutes les tables, ce qui efface tous les plans de requête. En fonction des facteurs mentionnés ci-dessus, si la première requête de la journée concerne un utilisateur avec seulement 1 enregistrement d'autorisation, SQL Server peut mettre en cache un plan qui fonctionne bien pour les utilisateurs avec 1 enregistrement mais fonctionne très bien avec les utilisateurs avec 20k enregistrements. Si la première requête de la journée concerne un utilisateur avec 20 000 enregistrements, vous pouvez obtenir un bon plan pour 20 000 enregistrements. Lorsque le code est exécuté sur un utilisateur avec 1 enregistrement, ce n'est peut-être pas la requête la plus optimale, mais elle peut quand même se terminer en ms. Cela ressemble vraiment à un reniflement de paramètre. Cela explique pourquoi vous ne voyez pas toujours le problème ou pourquoi il faut parfois des heures pour apparaître.
1. Le nouvel index résoudra-t-il le problème, ce qui l'empêchera-t-il de choisir à nouveau le mauvais plan?
Je pense que l'un des index que vous avez ajoutés évitera le problème car l'accès aux données requises via l'index coûtera moins cher que d'effectuer une analyse d'index en cluster sur la table, en particulier lorsque l'analyse ne peut pas se terminer tôt. Zoomons sur la mauvaise partie du plan de requête:
SQL Server estime qu'une seule ligne sera renvoyée par la jointure sur [Permission]
et [Project]
. Pour chaque ligne de l'entrée externe, il effectuera une analyse d'index en cluster [Appointment]
. Toutes les lignes seront analysées à partir de cette table, mais seules celles correspondant au filtrage [Start]
seront renvoyées à l'opérateur de jointure. Au sein de l'opérateur de jointure, les résultats sont encore réduits.
Le plan de requête décrit ci-dessus peut être correct s'il n'y a vraiment qu'une seule ligne envoyée à l'entrée externe de la jointure. Cependant, si l'estimation de cardinalité de la jointure est incorrecte et que nous obtenons, disons, 1 000 lignes, SQL Server effectuera 1 000 analyses d'index en cluster [Appointment]
. Les performances du plan de requête sont très sensibles aux problèmes d'estimation.
Le moyen le plus direct de ne plus jamais obtenir ce plan de requête consiste à créer un index de couverture sur la [Appointment]
table. Quelque chose comme un index [ProjectId]
et [Start]
devrait le faire. Il semble que ce soit exactement l' [idx_appointment_start]
index que vous avez créé pour résoudre le problème. Une autre façon de décourager SQL Server de choisir le plan de requête consiste à corriger l'estimation de cardinalité à partir de la jointure sur [Permission]
et [Project]
. Les moyens typiques de le faire incluent la modification du code, la mise à jour des statistiques, l'utilisation de l'héritage CE, la création de statistiques multi-colonnes, la fourniture de plus d'informations à SQL Server sur les variables locales, comme avec un RECOMPILE
indice, ou la matérialisation de ces lignes dans une table temporaire. Beaucoup de ces techniques ne sont pas une bonne approche lorsque vous avez besoin d'un temps de réponse de niveau ms ou devez écrire du code via un ORM.
L'index que vous avez créé [AppointmentAttendee]
n'est pas un moyen direct de résoudre le problème. Cependant, vous obtiendrez des statistiques multi-colonnes sur l'index et ces statistiques pourraient décourager le mauvais plan de requête. L'index peut fournir un moyen plus efficace d'accéder aux données, ce qui peut également décourager le mauvais plan de requête, mais je ne pense pas qu'il existe une quelconque garantie que cela ne se reproduira pas uniquement avec l'index activé [AppointmentAttendee]
.
3. Comment m'assurer que cela n'arrive pas à une autre requête / plan?
Je comprends pourquoi vous posez cette question, mais elle est extrêmement large. Mon seul conseil est d'essayer de mieux comprendre la cause première de l'instabilité du plan de requête, de valider que vous avez les bons index créés pour votre charge de travail et de tester et de surveiller soigneusement votre charge de travail. Microsoft a quelques conseils généraux sur la façon de gérer les régressions du plan de requête causées par le nouveau CE dans SQL Server 2016:
Le flux de travail recommandé pour la mise à niveau du processeur de requêtes vers la dernière version du code est le suivant:
Mettre à niveau une base de données vers SQL Server 2016 sans modifier le niveau de compatibilité de la base de données (conservez-le au niveau précédent)
Activez le magasin de requêtes sur la base de données. Pour plus d'informations sur l'activation et l'utilisation du magasin de requêtes, voir Surveillance des performances à l'aide du magasin de requêtes.
Attendez suffisamment de temps pour collecter des données représentatives de la charge de travail.
Changez le niveau de compatibilité de la base de données à 130
À l'aide de SQL Server Management Studio, évaluez s'il existe des régressions de performances sur des requêtes spécifiques après la modification du niveau de compatibilité
Pour les cas où il y a des régressions, forcez le plan précédent dans le magasin de requêtes.
S'il existe des plans de requête qui ne parviennent pas à forcer ou si les performances sont encore insuffisantes, pensez à rétablir le niveau de compatibilité au paramètre précédent, puis à engager le support client Microsoft.
Je ne dis pas que vous devez rétrograder vers SQL Server 2012 et recommencer, mais la technique générale décrite peut vous être utile.
2. Dois-je "forcer" le plan qui fonctionne bien maintenant?
Cela dépend entièrement de vous. Si vous pensez que vous disposez d'un plan de requête qui fonctionne bien pour tous les paramètres d'entrée possibles, que vous êtes à l'aise avec la fonctionnalité du magasin de requêtes et que vous souhaitez la tranquillité d'esprit associée au forçage d'un plan de requête, allez-y. Forcer les plans de requête qui avaient des régressions fait partie de la politique de mise à niveau recommandée par Microsoft vers SQL Server 2016 après tout.