Database
 sql >> Base de données >  >> RDS >> Database

Une approche du réglage d'index - Partie 2

Dans mon dernier message, j'ai commencé à décrire le processus que je traverse lors du réglage des requêtes, en particulier lorsque je découvre que je dois ajouter un nouvel index ou en modifier un existant. Jusqu'à présent, nous avons identifié la requête problématique, l'index dont j'ai besoin, les index qui existent actuellement sur la table et si ces index sont utilisés ou non. Une fois que nous avons ces données, nous pouvons passer aux étapes suivantes du processus.

Étape 5 :Qu'est-ce qui utilise un index ?

En plus de voir à quelle fréquence un index est utilisé (ou non), il est utile de savoir quelles requêtes utiliser un index, en particulier si je cherche à le fusionner avec un autre index. Heureusement, Jonathan Kehayias a déjà écrit une requête pour aider à identifier les plans qui utilisent un index spécifique. Sa version peut être utilisée pour le cache de plan - le seul défi est que les informations sont transitoires, vous ne pouvez donc pas capturer toutes les requêtes qui utilisent un index particulier. Query Store peut vous aider. J'ai modifié sa requête pour obtenir les mêmes informations à partir des plans de Query Store :

  SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
  DECLARE @IndexName AS NVARCHAR(128) = N'[IX_Sales_OrderLines_AllocatedStockItems]',
          @lb AS nchar(1) = N'[', @rb AS nchar(1) = N']';
 
  -- Make sure the name passed is appropriately quoted
  IF (LEFT(@IndexName, 1) <> @lb AND RIGHT(@IndexName, 1) <> @rb) SET @IndexName = QUOTENAME(@IndexName);
 
  --Handle the case where the left or right was quoted manually but not the opposite side
  IF LEFT(@IndexName, 1)  <> @lb SET @IndexName = @rb + @IndexName;
  IF RIGHT(@IndexName, 1) <> @rb SET @IndexName = @IndexName + @rb;
 
  ;WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')   
  SELECT
    stmt.value('(@StatementText)[1]', 'varchar(max)') AS SQL_Text,
    obj.value('(@Database)[1]', 'varchar(128)') AS DatabaseName,
    obj.value('(@Schema)[1]', 'varchar(128)') AS SchemaName,
    obj.value('(@Table)[1]', 'varchar(128)') AS TableName,
    obj.value('(@Index)[1]', 'varchar(128)') AS IndexName,
    obj.value('(@IndexKind)[1]', 'varchar(128)') AS IndexKind,
    query_plan
  FROM 	
  (
    SELECT query_plan
    FROM
    (
      SELECT TRY_CONVERT(XML, [qsp].[query_plan]) AS [query_plan]
      FROM sys.query_store_plan [qsp]
    ) tp
  ) AS tab (query_plan)
  CROSS APPLY query_plan.nodes('/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS batch(stmt)
  CROSS APPLY stmt.nodes('.//IndexScan/Object[@Index=sql:variable("@IndexName")]') AS idx(obj)
  OPTION(MAXDOP 1, RECOMPILE);

Il convient de noter qu'il s'agit d'un autre point où je peux me retrouver très profondément dans un terrier de lapin, en fonction du nombre d'index que j'examine et du nombre de requêtes qui les utilisent. Si possible, je prendrai également en compte le nombre d'exécutions (depuis le magasin de requêtes ou le cache du plan) pour ne pas simplement comprendre quoi requête utilise un index, mais à quelle fréquence cette requête s'exécute. C'est là que le réglage des index devient un art. Je peux collecter une quantité ridicule de données... mais je n'ai pas un temps infini pour l'analyse, donc je dois faire un jugement sur le nombre de requêtes que je vais examiner.

Étape 6 :Tests

Dans sa forme la plus simple, tester un index signifie prendre la requête problématique et capturer les données du plan et des performances (durée, IO, CPU, etc.), puis créer l'index, réexécuter la requête et capturer les mêmes informations. Si les performances s'améliorent, vous êtes prêt à partir !

C'est rarement aussi simple.

Pour commencer, j'ai souvent au moins deux variantes d'un indice que je veux tester, parfois plus. Je commence par ma ligne de base, puis je crée toutes les variantes d'index, efface le cache du plan et vois ce que SQL Server choisit. Ensuite, je parcours et force chaque index avec un indice, en capturant le plan et les mesures de performance pour chaque exécution. Remarque :cela suppose que j'ai suffisamment d'espace disque pour tous les index… sinon, je les crée un à la fois et je teste. Enfin, je compare les chiffres. Si j'ajoute simplement un nouvel index, j'ai presque terminé. Mais si je modifie un index ou en fusionne quelques-uns, cela peut devenir compliqué.

Dans un monde idéal, si je modifie un index existant, je trouve les requêtes les plus fréquentes/importantes qui utilisent l'index actuel et j'obtiens leurs plans et mesures de performance (c'est facile avec Query Store). Ensuite, je modifie l'index, exécute à nouveau toutes ces requêtes et vois si j'obtiens des changements significatifs dans la forme et/ou les performances du plan.

Si je fusionne deux index, je fais la même chose, mais avec toutes les requêtes qui utilisent l'un ou l'autre index, puis je teste à nouveau avec l'index fusionné.

Si j'ajoute/modifie/fusionne plusieurs index pour une table, je dois obtenir toutes les requêtes pertinentes et leurs plans et métriques, modifier les index, puis obtenir à nouveau toutes les informations et comparer. Cela peut prendre énormément de temps, selon le nombre de requêtes différentes. C'est là qu'il s'agit d'une forme d'art et vous devez déterminer le nombre de requêtes que vous devez vraiment tester. C'est une fonction de la fréquence d'exécution, de l'importance/pertinence de la requête et du temps dont je dispose/alloué.

Enfin, si j'ajoute un index à une table et que je n'en supprime aucun existant, j'ai ajouté une surcharge pour les INSERT, les DELETE et potentiellement les UPDATE. Tester les performances de cette modification est possible, mais vous avez besoin d'un environnement de test et de la capacité d'exécuter un test de charge et de capturer des métriques avant et après modification liées à la durée, aux E/S et au processeur.

C'est beaucoup d'amis, c'est pourquoi il est ironique que j'aie initialement pensé à dire que le réglage de l'index était facile. Ce n'est peut-être pas toujours simple, mais c'est possible. C'est une question de diligence et de suivi de tout cela.

Étape 7 :Mise en œuvre

Une fois que j'ai vérifié le ou les nouveaux index autant que possible, nous sommes prêts pour la production. J'avoue que je considère les changements d'indice comme à faible risque, en particulier les nouveaux. Si c'est un problème, vous pouvez le supprimer immédiatement et revenir à l'état d'origine. Avec un scénario de modification/fusion/suppression, vous souhaitez que tout soit scripté, de sorte que vous puissiez modifier et recréer les index si nécessaire pour réinitialiser les index. Je recommande toujours de désactiver initialement les index au lieu de les supprimer, car alors vous n'avez pas à vous soucier de la définition - si vous avez besoin de rajouter l'index, vous le reconstruisez simplement.

Résumé

Votre méthode d'ajout et/ou de consolidation des index peut être différente ! Tout comme le réglage des requêtes, il n'y a pas de processus parfait. Pour toute personne novice en matière de réglage d'index, cela fournit, espérons-le, un aperçu des éléments à examiner et des considérations importantes. Il est impossible d'ajouter des index sans ajouter une certaine quantité de frais généraux - et encore une fois, c'est là que l'art entre en jeu :vous devez déterminer si les avantages de l'index l'emportent sur son coût pour les modifications.

Le réglage de l'index est un processus perpétuel et itératif - je ne pense pas que vous ayez jamais terminé, car des modifications de code, de nouvelles tables ou fonctionnalités sont ajoutées et les données des tables changent. Kimberly a deux messages (https://www.sqlskills.com/blogs/kimberly/spring-cleaning-your-indexes-part-i/ et https://www.sqlskills.com/blogs/kimberly/spring-cleaning- your-indexes-part-ii/) qui parlent de nettoyer vos index — c'est le moment de commencer ! Et enfin, chaque fois que quelqu'un demande, "combien d'index devrait-il y avoir pour une table?" Je réponds par quelque chose comme "le moins de nombre dont vous avez besoin pour satisfaire autant de requêtes que possible". Il n'y a pas de nombre magique - j'ai vu des tables avec zéro index, et j'ai vu des tables avec plus de 100 (je suis sûr que certains d'entre vous ont vu des nombres plus élevés). Ni zéro ni 100 ne sont bons, mais le « bon » nombre est celui que vous devez déterminer à l'aide des données disponibles et de votre expérience.