février
2011
S’il est vrai que les index (les bons) participent à l’amélioration des performances d’une base de données,il ne faut pas perdre de vue que sa maintenance à un coût et donc pénalisant pour la performance. Alors comment trouver, au profit de la performance, le juste milieu ?. Voici une série de requêtes qui permettent de :
–> Afficher le nombre d’index manquants par base sur une instance
–> Afficher les index manquants et leur bénéfice
–> Afficher le coût des index non utilisés pour décider de leur suppression ou pas.
–> Mettre en perspective les index utilisés et leur coût de maintenance
/*========================================================================================================================
– Titre : Nombre d’index manquants par base de données
— Desciption : Afficher le nombre d’indexes manquants par bases de données
— Auteur : Etienne ZINZINDOHOUE
========================================================================================================================*/
SELECT
DB_NAME(database_id) AS [Base]
,count(*) AS [Nombre Indexes Manquants]
FROM sys.dm_db_missing_index_details
GROUP BY DB_NAME(database_id)
ORDER BY [Nombre Indexes Manquants] DESC;
/*========================================================================================================================
– Titre : Quel est le bénéfice d’un index manquant ?
— Desciption : Afficher les 20 premiers index manquants et leurs bénéfices
— Auteur : Etienne ZINZINDOHOUE
=========================================================================================================================*/
SELECT TOP 20
ROUND(avg_total_user_cost * avg_user_impact * (user_seeks + user_scans)*0.01,1) AS [Bénéfice Cumulé (%)] --Bénéfice cumulé pour les requêtes utilisateurs
,avg_user_impact AS [Bénéfice Moyen (%)] --Bénéfice moyen (en pourcentage) dont les requêtes utilisateur pourraient tirer parti si ce groupe d'index manquants était implémenté. Cela signifie que le coût des requêtes diminuerait, en moyenne, de la valeur de ce pourcentage si ce groupe d'index manquants était implémenté.
, [statement] AS [Table]
, equality_columns AS [ListeColonnesEgalite] -- Liste de colonnes, séparées par des virgules, qui contribuent aux prédicats d'égalité au format :table.column = constant_value
, inequality_columns AS [ListeColonnesInegalite] -- Liste de colonnes, séparées par des virgules, qui contribuent aux prédicats d'inégalité, par exemple, les prédicats au format : table.column > constant_value
, included_columns AS [ListeColonnesCouverture] -- Liste de colonnes, séparées par des virgules, requises comme colonnes de couverture pour la requête
FROM sys.dm_db_missing_index_groups g
INNER JOIN sys.dm_db_missing_index_group_stats s
ON s.group_handle = g.index_group_handle
INNER JOIN sys.dm_db_missing_index_details d
ON d.index_handle = g.index_handle
ORDER BY [Bénéfice Cumulé (%)] DESC;
/*========================================================================================================================
-- Titre : Coût des index non utilisés
— Description : Afficher les 20 premiers index non utilisés et leur coût de maintenance. ces index non utilisés faut-il les supprimés ?
— Auteur : Etienne ZINZINDOHOUE
========================================================================================================================*/
--Les variables
DECLARE @Nom_BaseEnCoursTraitement VARCHAR(50),@ID_BaseEnCoursTraitement VARCHAR(50), @SQL VARCHAR (max), @DEL VARCHAR (max)
--Créer et alimenter la table #LISTE_BASES avec le nom des bases (sauf les bases systèmes) de l'instance
SELECT database_id,name
INTO #LISTE_BASES
FROM sys.databases
WHERE name not in ('master','model','msdb','temp')
ORDER BY [database_id]
--Créer la table #TempIndexesInutilises des indexes non utilisés
SELECT TOP 1
DB_NAME() AS [Base]
,OBJECT_NAME(s.[object_id]) AS [Table]
,i.name AS [Index]
,user_updates AS [UserRequetesUpdates]
,system_updates AS [SystemRequetesUpdates]
INTO #TempIndexesInutilises
FROM sys.dm_db_index_usage_stats s
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id]
AND s.index_id = i.index_id
WHERE s.database_id = DB_ID()
AND OBJECTPROPERTY(s.[object_id], 'IsMsShipped') = 0
AND user_seeks = 0
AND user_scans = 0
AND user_lookups = 0
AND s.[object_id] = - 333 -- Numéro bidon, c'est juste pour recupérer la structure de la table.
;
-- Tant qu'il y a des bases à traitées
WHILE EXISTS (SELECT database_id FROM #LISTE_BASES )
BEGIN
--Se positionner sur la 1ère ligne de la table temporaire
SET @ID_BaseEnCoursTraitement = CAST((SELECT TOP 1 database_id FROM #LISTE_BASES)AS int)
SET @Nom_BaseEnCoursTraitement = (SELECT TOP 1 [name] FROM #LISTE_BASES)
-- Insert les infos de la base en cours de traitement dans la table de sortie
SET @SQL = N'USE '+ @Nom_BaseEnCoursTraitement + '; INSERT INTO #TempIndexesInutilises
SELECT TOP 20
DB_NAME()
,OBJECT_NAME(s.[object_id])
,i.name
,user_updates -- Nombre de mises à jour par requête utilisateur
,system_updates -- Nombre de mises à jour par requête système
FROM sys.dm_db_index_usage_stats s
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] AND s.index_id = i.index_id
WHERE s.database_id = ' + @ID_BaseEnCoursTraitement +'
AND OBJECTPROPERTY(s.[object_id], ''IsMsShipped'') = 0 -- objet créé lors de l''installation de SQL Server -- 0 = FAUX et 1 = Vrai
AND s.user_seeks = 0 -- Nombre de recherches résultant de requêtes utilisateur pour lesquelles l''index recommandé du groupe pourrait avoir été utilisé
AND s.user_scans = 0 -- Nombre d''analyses résultant de requêtes utilisateur pour lesquelles l''index recommandé du groupe pourrait avoir été utilisé
AND s.user_lookups = 0 -- Nombre de recherches de signets par les requêtes utilisateur
AND i.name IS NOT NULL
ORDER BY user_updates DESC ;'
EXEC (@SQL);
-- Supprimer la base traitée de la table temporaire
SET @DEL = 'DELETE FROM #LISTE_BASES WHERE database_id = '+ @ID_BaseEnCoursTraitement +';'
EXEC (@DEL)
END
-- Afficher les résultats
SELECT TOP 20 [Base]
,[Table]
,[Index]
,[UserRequetesUpdates] AS [MAJ index par requête utilisateur]
,[SystemRequetesUpdates] AS [MAJ index par requête système]
FROM #TempIndexesInutilises ORDER BY [UserRequetesUpdates] DESC
--Supprimer les tables temporaires
DROP TABLE #LISTE_BASES
DROP TABLE #TempIndexesInutilises
Remarques : /!\ Pas de précipitation ! Avant de supprimer un index qui n’est pas utilisé, il faut d’abord bien se renseigné pour savoir à quel moment cet index pourrait être utile. Attention donc !
/*========================================================================================================================
– Titre : Mise en perspective de l’utilisation d’un index et son coût de maintenance
— Description : S’il est vrai que les index (les bons) participent à l’amélioration des performances d’une base de donnée il ne faut pas perdre de vue que leurs maintenances a un coût et donc peuvent pénaliser les performances. Alors comment trouver, au profit de la performance, le juste milieu ?
— Auteur : Etienne ZINZINDOHOUE
========================================================================================================================*/
DECLARE @Nom_BaseEnCoursTraitement VARCHAR(50),@ID_BaseEnCoursTraitement VARCHAR(50) , @SQL VARCHAR (max), @DEL VARCHAR (max)
--Créer la table #LISTE_BASES liste des bases
SELECT database_id,name
-- Alimenter la table liste des bases #LISTE_BASES
INTO #LISTE_BASES
FROM sys.databases
WHERE name not in ('master','model','msdb','temp')
ORDER BY [database_id]
--Créer la table #TempIndexesUtilises des indexes non utilisés
SELECT TOP 1
(user_updates + system_updates) AS [Coût Maintenance]
,(user_seeks + user_scans + user_lookups) AS [Fréquence Utilisation]
,DB_NAME() AS [Base]
,OBJECT_NAME(s.[object_id]) AS [Table]
,i.name AS [Index]
INTO #TempIndexesUtilises
FROM sys.dm_db_index_usage_stats s
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] AND s.index_id = i.index_id
WHERE s.database_id = DB_ID()
AND OBJECTPROPERTY(s.[object_id], 'IsMSShipped') = 0
AND (user_updates + system_updates) > 0 -- (Nombre de mises à jour par requête utilisateur + Nombre de mises à jour par requête système) >0
AND s.[object_id] = - 333 -- Juste pour recuperer la structure de la table.
;
-- Tant qu'il y a des bases à traitées
WHILE EXISTS (SELECT database_id FROM #LISTE_BASES )
BEGIN
--Se positionner sur la 1ère ligne de la table temporaire
SET @ID_BaseEnCoursTraitement = CAST((SELECT TOP 1 database_id FROM #LISTE_BASES)AS int)
SET @Nom_BaseEnCoursTraitement = (SELECT TOP 1 [name] FROM #LISTE_BASES)
-- Insert les infos de la table en cours de traitement dans la table de sortie
SET @SQL = N'USE '+ @Nom_BaseEnCoursTraitement + '; INSERT INTO #TempIndexesUtilises
SELECT TOP 20
(user_updates + system_updates) AS [Coût Maintenance] -- (Nombre de mises à jour par requête utilisateur + Nombre de mises à jour par requête système) >0
,(user_seeks + user_scans + user_lookups) AS [Fréquence Utilisation]-- Nombre de recherches résultant de : requêtes utilisateurs,requêtes d''analyses et de signets
,DB_NAME() AS [Base]
,OBJECT_NAME(s.[object_id]) AS [Table]
,i.name AS [Index]
FROM sys.dm_db_index_usage_stats s
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] AND s.index_id = i.index_id
WHERE s.database_id = ' + @ID_BaseEnCoursTraitement +'
AND OBJECTPROPERTY(s.[object_id], ''IsMsShipped'') = 0 -- objet créé lors de l''installation de SQL Server -- 0 = FAUX et 1 = Vrai
AND (user_updates + system_updates) > 0 -- (Nombre de mises à jour par requête utilisateur + Nombre de mises à jour par requête système) >0
ORDER BY user_updates DESC ;'
EXEC (@SQL);
-- Supprimer la base traitée de la table temporaire
SET @DEL = 'DELETE FROM #LISTE_BASES WHERE database_id = '+ @ID_BaseEnCoursTraitement +';'
EXEC (@DEL)
END
-- Mettre en perspective le coût de la maintenance et l'utilité
SELECT TOP 20 DENSE_RANK() OVER(ORDER BY [Coût Maintenance]DESC) AS [Classement Coût Maintenance]
,DENSE_RANK() OVER(ORDER BY [Fréquence Utilisation] DESC) AS [Classement Fréquence Utilisation]
,[Coût Maintenance]
,[Fréquence Utilisation],[Base],[Table],[Index]
FROM #TempIndexesUtilises
ORDER BY [Coût Maintenance] DESC
--Supprimer les tables temporaires
DROP TABLE #LISTE_BASES
DROP TABLE #TempIndexesUtilises
Maintenance des index
===============================================
Pour la maintenance des index SQLPro a fait un travail génial à ce sujet
Merci à SQLPro
Dans mon cas nous utilisons une suite logicielle achetée, donc je ne dispose que d’informations partielles sur les requêtes réellement utilisées par l’application. L’éditeur n’ayant pas de ressource DBA sur SqlServer, je me retrouve seul à gérer le problème des performances de mon serveur.
—————-
>> Pour savoir les requêtes exécutées par l’application, tu peux utiliser SQL Server Profiler afin de tracer tout ce qui se passe sur le serveur de base de données.
Tu peux aussi utiliser les DMVs/DMFs afin de cibler les requêtes en fonctions des indicateurs de performances que tu souhaites. J’ai écrit des requêtes qui te permettent de savoir par exemple :
– les requêtes les plus consommatrices de CPU
– les requêtes les + fréquemment exécutées
– les requêtes les + coûteuses en I/O logique (Lecture/Ecriture de données en mémoire)
-….
Tu peux trouver ces requêtes à l’Url : http://blog.developpez.com/zinzineti/p9075/sql-server-2005/informations-sur-les-requetes/
—————-
Avec la requête « Quel est le bénéfice d’un index manquant ? », j’ai pu identifier plus de 120 index manquants que j’ai rajouter en suivant l’article microsoft cité précédemment. Le gain c’est mesuré immédiatement en exploitation. Initialement, je me doutais bien que l’éditeur n’avait quasiment rien optimiser et qu’il me fallait rajouter des index, sans pour autant savoir comment m’y prendre.
—————-
>> Il faut mettre en place un plan de maintenance des index crées.
—————-
– Sur la requête « Coût des index non utilisés », j’ai du mal à cerner ce que représente la valeur « [MAJ index par requête utilisateur] « . (en ce qui me concerne seule cette colonne a une valeur, « [MAJ index par requête système] » est à zéro)
—————-
>> un index créé et qui n’est pas utilisé est coûteux pour les performances : occupation mémoire, disque,…. c’est un peu comme si vous recrutez quelqu’un pour faire un travail et que vous vous rendez compte du fait que cette personne recrutée ne fait pas l’affaire. Qu’allez vous faire dans ce cas ? je vous laisse prendre la décision qui vous semble être juste.
Noté qu’à chaque utilisation d’un index les statistiques, la date de dernière utilisation, …. sont mis à jour. Plus un index est MAJ celà veut qu’il est utilisé.
Si c’est ton application qui utilise l’index lors de l’exécution d’une requête => »[MAJ index par requête utilisateur] «
Si des requêtes systèmes (procédures systèmes, fonctions systèmes, …) utilisent en interne un index => »[MAJ index par requête système] »
—————-
– Sur la requête de l’utilisation d’un index et son coût de maintenance, je me retrouve avec des résultats sur certaines tables, pour lesquelles aucun index n’est cité (Ex : 1,1,16107,121786,,,NULL).
—————-
>> Si aucun index n’est cité => ne correspond pas des index que tu as créé. Pas de souci ! les valeurs ,,,NULL affichées concernent les colonnes [Base],[Table],[Index] (données utilisateurs et non systèmes).
A+
Bonjour,
Dans mon cas nous utilisons une suite logicielle achetée, donc je ne dispose que d’informations partielles sur les requêtes réellement utilisées par l’application. L’éditeur n’ayant pas de ressource DBA sur SqlServer, je me retrouve seul à gérer le problème des performances de mon serveur.
Avec la requête « Quel est le bénéfice d’un index manquant ? », j’ai pu identifier plus de 120 index manquants que j’ai rajouter en suivant l’article microsoft cité précédemment. Le gain c’est mesuré immédiatement en exploitation. Initialement, je me doutais bien que l’éditeur n’avait quasiment rien optimiser et qu’il me fallait rajouter des index, sans pour autant savoir comment m’y prendre.
J’ai cependant deux questions concernant les autres requêtes d’optimisation :
– Sur la requête « Coût des index non utilisés », j’ai du mal à cerner ce que représente la valeur « [MAJ index par requête utilisateur] « . (en ce qui me concerne seule cette colonne a une valeur, « [MAJ index par requête système] » est à zéro)
– Sur la requête de l’utilisation d’un index et son coût de maintenance, je me retrouve avec des résultats sur certaines tables, pour lesquelles aucun index n’est cité (Ex : 1,1,16107,121786,,,NULL).
N’étant pas DBA (mais ayant récupérer ce rôle), j’ai du mal à interpréter ce résultat.
Il n’y a pas de recette magique pour booster l’exécution des requêtes. Mais créer les bons index sur les bonnes colonnes d’un modèle de données bien taillé, optimise le temps d’exécution des requêtes (requêtes bien écrites bien sûr !). Actions à mener :
0.) relever initial des indicateurs de performances des requêtes à optimisées
1.) créer les index sur les bonnes colonnes
2.) tester,analyser, relever à nouveau les indicateurs de performances des requêtes
3.) approuver (ou non) l’efficacité des index créés
A+
En recherchant les vues systèmes présentes dans la requête, j’ai trouvé la réponse dans la base « technet ». Chaque colonne a une utilité pour la création de l’index.
http://technet.microsoft.com/fr-fr/library/ms345405%28SQL.90%29.aspx
Bonjour,
Je trouve cet article très intéressant, il m’a permis de découvrir un certain nombre de chose sur le thème « Pourquoi mon serveur rame ».
J’aurais aimé avoir quelques détails sur la requête « Titre : Quel est le bénéfice d’un index manquant ? ».
Il me sort effectivement la liste des index manquants avec un bénéfice moyen à 99% pour une bonne dizaine d’entre elle.
Ma question est simple, en fonction des champs fournis dans les 3 colonnes, quelles sont les actions à mener pour créer les bons index ?
Car j’ai effectivement des champs retournés par la requête sur lesquels j’ai déjà des index (mais peut être mal déclarés/paramétrés). Et étant relativement novice sur le sujet j’ai un peu de mal à voir quelles modifications sont à faire pour optimiser la chose.
Merci d’avance.