SQL Server: couvrant les index, y compris toutes les colonnes?

9

Notre équipe a hérité d'une application et d'une base de données associée. Les développeurs précédents semblent avoir appliqué une règle selon laquelle chaque index, sur chaque table, a une clause INCLUDE pour toujours ajouter chaque colonne qui ne fait pas autrement partie de la clé. Ces tables ont en moyenne entre deux et cinq index ou contraintes uniques ainsi que des clés étrangères.

L'intention semble être d'améliorer les performances SELECT quelle que soit la requête lancée dans la base de données, car l'accès se fait via un ORM qui récupère par défaut (mais pas toujours) toutes les colonnes. Nous nous attendons à ce que les effets secondaires de cela soient des exigences de stockage accrues (éventuellement de manière significative) et un temps supplémentaire pour INSERT / UPDATE / DELETE.

La question est, est-ce une stratégie sensée? Notre équipe a des antécédents avec SQL Server mais aucun membre qui se considérerait comme un expert de son comportement interne (bien que la question ait été soulevée que si cette stratégie était optimale, ne serait-ce pas la valeur par défaut maintenant?). À quels autres effets secondaires (CPU / mémoire du serveur de base de données / utilisation TempDB, etc.) devrions-nous nous attendre, ou certaines de nos hypothèses ci-dessus sont-elles incorrectes?

De plus, l'application peut être installée à la fois sur SQL Server sur site (versions depuis 2012), ainsi qu'Azure SQL - si nous sommes préparés à toute différence entre les deux, ou à des effets secondaires supplémentaires sur Azure, à la suite de cela approche?

T2PS
la source

Réponses:

8

J'ai déjà fait cela sur des index spécifiques pour faciliter les requêtes lourdes souvent exécutées. En fait, ce qu'ils ont fait est de créer plusieurs index clusterisés: lorsque l'un de ces index est utilisé pour trouver des lignes, aucun travail supplémentaire n'est nécessaire en recherchant le reste des données dans l'index cluster réel (ou le tas s'il n'y a pas d'index cluster réel) .

est-ce une stratégie sensée?

Pour certains index où il était nécessaire de prendre en charge certains modèles de requête, certainement oui.

Mais pour ce faire avec tous les index, je dirais tout aussi certainement non.

Cela va être un gaspillage d'espace à faire là où ce n'est pas réellement nécessaire, et ralentira considérablement les insertions / mises à jour. Cela peut ralentir autant de requêtes de lecture que cela aide, car chaque page d'index contient moins d'enregistrements, de sorte que toute requête devant référencer un morceau de l'index pour le filtrage mais n'utilisant pas toutes les autres colonnes devra accéder à plus de pages. Cela rendra votre base de données plus gourmande en mémoire: ces pages devront être chargées dans le pool de mémoire tampon, éjectant potentiellement d'autres pages utiles si la mémoire est insuffisante. Si la compression est utilisée sur ces index pour essayer d'atténuer l'effet sur les exigences de stockage et de mémoire, elle poussera à la place une charge supplémentaire vers les CPU.

car l'accès se fait via un ORM qui récupère par défaut (mais pas toujours) toutes les colonnes

Il s'agit d'un modèle courant avec une utilisation mal optimisée d'un ORM (ou simplement des ORM naïfs) et dans ces cas, j'ai vu le conseiller d'index de SQL Server (et des outils tiers similaires) suggérer des index avec de nombreuses INCLUDEcolonnes d, donc je serais d'accord avec votre suggestion que c'est pourquoi les index ont été créés de cette façon.

Mais bien que cela puisse rendre toutes ces requêtes un peu plus rapides et certaines beaucoup plus rapides, je soupçonne que dans de nombreux cas, tout avantage est si petit qu'il ne vaut pas l'empreinte mémoire supplémentaire requise par votre ensemble de travail commun, l'espace sur le disque et l'E / S entre le disque et la mémoire.

N'oubliez pas également que l'ORM ne sélectionne peut-être pas toutes les colonnes de toutes les tables touchées par une requête, de sorte que l'avantage ne peut s'appliquer qu'à la cible principale de la demande actuelle, et les index plus grands peuvent pénaliser la requête lorsque d'autres objets sont utilisés pour le filtrage. mais ne renvoyant pas de données ( SELECT * FROM table1 WHERE id IN (SELECT someID FROM table2 WHERE someColumn='DesiredValue')peut-être).

Une autre considération pour l'espace excédentaire utilisé, en particulier si les données sont volumineuses, est que cela aura un impact sur votre stratégie de sauvegarde: les coûts de stockage et de transfert pour ces sauvegardes, les temps de restauration potentiels, etc.

devrions-nous être préparés à toute différence entre les deux [sur site et AzureSQL]

En général, je pense que les considérations ici seront les mêmes dans chaque cas, bien que tout excès de mémoire / coût d'E / S imposé par les grands index puisse être plus directement visible dans Azure où vous pouvez modifier le niveau de service et donc le coût de l'infrastructure plus facilement plutôt que ayant un ensemble relativement fixe de ressources matérielles. Si vous utilisez des niveaux standard / premium au lieu de la tarification basée sur vcore, vous serez davantage affecté par le coût des E / S en standard, car la prime inclut beaucoup plus d'E / S par DTU. Si vous utilisez des sauvegardes multirégionales ou de la redondance ou d'autres fonctionnalités non locales dans Azure, il peut y avoir un coût de bande passante associé à l'espace supplémentaire pris par des index de largeur excessive.

David Spillett
la source
Nous sommes allés de l'avant et avons fait cette suppression. Un effet secondaire a été que sur certaines tables, SELECTsans spécifier, a ORDER BYcommencé à retourner les mêmes lignes qu'avant mais avec un ordre arbitraire différent.
T2PS
Ce n'est pas inattendu. L'ordre des résultats sans 'ORDER BY' est par définition indéfini et peut changer à chaque fois que le planificateur de requêtes décide d'adopter une approche différente, ce qu'il peut faire à la suite de modifications d'index ou de modifications de vos modèles de données au fur et à mesure de leur croissance. D'autres facteurs peuvent modifier cette commande à une date ultérieure, même sans cette modification. Si vous comptez sur l'ordre de la sortie d'une instruction, même superficiellement, vous devez inclure un 'ORDER BY' pour le garantir.
David Spillett
Oh, définitivement. Le commentaire précédent était plutôt une note de rappel pour quiconque trouverait cette réponse plus tard.
T2PS
5

La question est, est-ce une stratégie sensée? .... (bien que la question ait été posée que si cette stratégie était optimale, ne serait-ce pas la valeur par défaut maintenant?)

Dans la plupart des cas, ce n'est pas une stratégie sensée. La raison en est que, dans les bases de données OLTP générales, les lignes renvoyées à l'utilisateur final ne seront pas très nombreuses. (Généralisation)

La question que vous devez vous poser est la suivante: si vous recherchez sur les colonnes clés, combien de lignes seront retournées par cette opération de recherche? Et répétez cela pour les requêtes cherchant sur cette colonne.

Considérez le tableau suivant, renvoyant un tas de colonnes, where SelectiveIDField= ...

select columnA,columnC, ... columnZ
FROM dbo.BigTable
Where SelectiveIDField= '225122141';

Si une seule ligne est renvoyée par la recherche selectiveIDField, la recherche de clé supplémentaire est-elle une si mauvaise chose? (en supposant que vous avez des index clusterisés ici, sinon recherche RID)

Il fera juste une recherche de clé supplémentaire, une exécution supplémentaire + l'opérateur de jointure. Même si c'est 10 ou même 100, est-ce que cela aurait un impact énorme? Cela dépend également de la quantité d'exécution de votre requête et de l'importance du temps d'exécution.

Dans le cas où il est négligeable, créez simplement l'indice SelectiveIDFieldet appelez-le un jour, il ne devrait pas valoir les gains en lecture par rapport aux pertes en écriture.

Donc, en bref, la création d'index sur la table entière ne devrait pas, à mon avis, être une approche par défaut, sauf si vous voyez vraiment un problème avec une requête et que vous pouvez l'améliorer considérablement en ajoutant un index de couverture entier.

Randi Vertongen
la source