Ai-je besoin d'index séparés pour chaque type de requête, ou un index multi-colonnes fonctionnera-t-il?

22

Je connais déjà un peu la réponse à cette question, mais j'ai toujours l'impression qu'il me faut en savoir plus sur le sujet.

Ma compréhension de base est que, de manière générale, un index unique qui inclut uniquement tous les champs sur lesquels vous pouvez interroger / trier à un moment donné n'est probablement pas utile, mais j'ai vu ce type de chose. Comme dans, quelqu'un a pensé: "Eh bien, si nous plaçons tout cela dans un index, la base de données peut l'utiliser pour trouver ce dont il a besoin", sans jamais avoir vu de plan d'exécution pour certaines des requêtes en cours d'exécution.

Imaginez une table comme ça:

id int pk/uid
name varchar(50)
customerId int (foreign key)
dateCreated datetime

Je pourrais voir un seul indice , y compris les name, customerIdet les dateCreatedchamps.

Mais ma compréhension est qu'un tel index ne serait pas utilisé dans une requête comme, par exemple:

SELECT [id], [name], [customerId], [dateCreated]
   FROM Representatives WHERE customerId=1 
   ORDER BY dateCreated

Pour une telle requête, il me semble qu'une meilleure idée serait un index incluant les champs customerIdet dateCreated, le customerIdchamp étant «premier». Cela créerait un index qui aurait les données organisées de telle manière que cette requête pourrait trouver rapidement ce dont elle a besoin - dans l'ordre dont elle a besoin.

Une autre chose que je vois, peut-être aussi souvent que la première, ce sont les index individuels sur chaque champ; ainsi, une sur chaque name, customerIdet les dateCreatedchamps.

Contrairement au premier exemple, ce type d'arrangement me semble parfois au moins partiellement utile; le plan d'exécution de la requête peut montrer qu'au moins il utilise l'index sur le customerIdpour sélectionner les enregistrements, mais il n'utilise pas l'index avec le dateCreatedchamp pour les trier.


Je sais que c'est une question large, car la réponse spécifique à une requête particulière sur un ensemble particulier de tables est généralement de voir ce que le plan d'exécution dit qu'il va faire, et sinon prendre en compte les spécificités des tables et des requêtes Compte. En outre, je sais que cela dépend de la fréquence à laquelle une requête peut être exécutée, par opposition à la surcharge de maintenance d'un index particulier pour elle.

Mais je suppose que ce que je demande, c'est comme un «point de départ» général pour les index, est-ce que l'idée d'avoir des index spécifiques pour des requêtes spécifiques et fréquemment tirées et les champs dans les clauses WHERE ou ORDER BY est logique?

Andrew Barber
la source

Réponses:

27

Vous avez raison en ce que votre exemple de requête n'utiliserait pas cet index.

Le planificateur de requêtes envisagera d'utiliser un index si:

  • tous les champs qu'il contient sont référencés dans la requête
  • certains des champs commençant par le début sont référencés

Il ne pourra pas utiliser d'index commençant par un champ non utilisé par la requête.

Donc pour votre exemple:

SELECT [id], [name], [customerId], [dateCreated]
   FROM Representatives WHERE customerId=1 
   ORDER BY dateCreated

il envisagerait des index tels que:

[customerId]
[customerId], [dateCreated]
[customerId], [dateCreated], [name]

mais non:

[name], [customerId], [dateCreated]

S'il trouvait les deux [customerId]et [customerId], [dateCreated], [name]sa décision de préférer l'un plutôt que l'autre dépendrait des statistiques de l'indice qui dépendent des estimations de l'équilibre des données dans les champs. Si elles [customerId], [dateCreated]ont été définies, elles devraient préférer cela aux deux autres, sauf indication contraire.

Il n'est pas rare de voir un index défini pour chaque champ dans mon expérience, bien que ce soit rarement optimal car la gestion supplémentaire nécessaire pour mettre à jour les index lors de l'insertion / mise à jour, et l'espace supplémentaire nécessaire pour les stocker, est gaspillé lorsque la moitié de ils peuvent ne jamais être utilisés - mais à moins que votre base de données ne voit des charges lourdes en écriture, les performances ne vont pas mal, même avec les index excessifs.

Des index spécifiques pour les requêtes fréquentes qui seraient autrement lentes en raison de l'analyse des tables ou des index sont généralement une bonne idée, mais n'en faites pas trop car vous pourriez échanger un problème de performances contre un autre. Si vous définissez [customerId], [dateCreated]un index, par exemple, n'oubliez pas que le planificateur de requêtes pourra l'utiliser pour les requêtes qui utiliseraient un index juste [customerId]s'il était présent. Bien que l'utilisation juste [customerId]soit légèrement plus efficace que l'utilisation de l'index composé, cela peut être atténué en finissant par avoir deux index en compétition pour l'espace en RAM au lieu d'un (bien que si votre ensemble de travail normal s'intègre facilement dans la RAM, cette compétition de mémoire supplémentaire peut ne pas être un problème).

David Spillett
la source
+1; grande info, en particulier le rappel (que j'ai tendance à oublier!) que le planificateur peut utiliser un index composé à des moments où il n'a besoin que du ou des premiers champs de celui-ci pour une requête.
Andrew Barber le
6

Pour répondre à votre question d'origine, oui, les index doivent être conçus autour des requêtes , pas seulement de la table . L'ordre des champs dans l'index est d'une importance vitale. Concevoir un index unique pour être optimal pour plusieurs requêtes est plus difficile, et vous devrez faire des compromis.

En ce qui concerne votre deuxième point, oui, un tas d'index sur des champs individuels est ennuyeusement commun. Je le vois tout le temps dans mon environnement, et c'est généralement un drapeau rouge pour moi que l'équipe de développement n'a pas travaillé avec un DBA pour concevoir des index appropriés.

Ma stratégie pour concevoir des index, est d'indexer:

  • Champs utilisés dans WHERE (par ordre de sélectivité)
  • Champs utilisés dans ORDER BY
  • Inclure d'autres champs (si nécessaire) pour faire un index de couverture

Donc pour votre exemple:

SELECT [id], [name], [customerId], [dateCreated]
   FROM Representatives WHERE customerId=1 
   ORDER BY dateCreated

Je voudrais probablement concevoir un index sur (CustomerID, dateCreated) INCLUDE (id, name). Cet index de couverture signifie que la requête n'a jamais besoin d'atteindre la table d'origine, améliorant considérablement les performances.

Cependant, cet exemple est presque trop simple. Un index naïf sur juste (CustomerID) fonctionnerait presque aussi bien (en supposant que chaque client n'a qu'un seul représentant, donc une seule recherche de signet dans la table sera nécessaire). Il peut également être utile de réellement faire un index clusterisé sur (CustomerID, ID), en fonction des autres requêtes exécutées sur la table.

BradC
la source
+1 pour "les index doivent être conçus autour des requêtes, pas seulement de la table", et du reste de la réponse, par exemple en notant que l'exemple est très simple.
Andrew Barber,