web-dev-qa-db-fra.com

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

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 de SELECT, quelle que soit la requête lancée sur 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 (utilisation du processeur/de la mémoire du serveur de base de données/de 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 nous préparons à toute différence entre les deux, ou à des effets secondaires supplémentaires sur Azure, à la suite de cela approche?

9
T2PS

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

C'est 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 colonnes INCLUDEd, donc Je suis d'accord avec votre suggestion selon laquelle c'est la raison pour laquelle 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 le IO 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 IO en standard car la prime inclut beaucoup plus IO 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.

8
David Spillett

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 qu'en général OLTP bases de données, les lignes renvoyées à l'utilisateur final ne vont pas être beaucoup. (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 grand nombre de colonnes, where SelectiveIDField= ...

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

Si une seule ligne sera retournée par la recherche sur 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, il suffit de créer l'index sur SelectiveIDField et de l'appeler 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 à mon avis pas ê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.

5
Randi Vertongen