Lorsque vous avez une requête ou une procédure stockée qui nécessite un réglage des performances, quelles sont les premières choses que vous essayez?
Lorsque vous avez une requête ou une procédure stockée qui nécessite un réglage des performances, quelles sont les premières choses que vous essayez?
Réponses:
Voici la liste pratique des choses que je donne toujours à quelqu'un qui me pose des questions sur l'optimisation.
Nous utilisons principalement Sybase, mais la plupart des conseils s'appliqueront à tous les niveaux.
SQL Server, par exemple, est livré avec une multitude de bits de surveillance / réglage des performances, mais si vous n'avez rien de tel (et peut-être même si vous le faites), je considérerais ce qui suit ...
99% des problèmes que j'ai vus sont causés par le fait de placer trop de tables dans une jointure . Le correctif pour cela est de faire la moitié de la jointure (avec certaines des tables) et de mettre en cache les résultats dans une table temporaire. Ensuite, effectuez le reste de la requête en joignant cette table temporaire.
#temp
les tables peuvent être bien meilleures que les @table
variables avec de gros volumes (des milliers de lignes).Un peu hors sujet mais si vous avez le contrôle sur ces problèmes ...
Haut niveau et fort impact.
CREATE INDEX
Assurez-vous qu'il existe des index disponibles pour vos clauses WHERE
et JOIN
. Cela accélérera considérablement l'accès aux données.
Si votre environnement est un data mart ou un entrepôt, les index devraient abonder pour presque toutes les requêtes imaginables.
Dans un environnement transactionnel , le nombre d'index doit être inférieur et leurs définitions plus stratégiques afin que la maintenance des index ne diminue pas les ressources. (La maintenance d'index se produit lorsque les feuilles d'un index doivent être modifiées pour refléter une modification de la table sous-jacente, comme avec les opérations INSERT, UPDATE,
et DELETE
.)
Soyez également attentif à l'ordre des champs dans l'index - plus un champ est sélectif (cardinalité élevée), plus il doit apparaître tôt dans l'index. Par exemple, disons que vous recherchez des voitures d'occasion:
SELECT i.make, i.model, i.price
FROM dbo.inventory i
WHERE i.color = 'red'
AND i.price BETWEEN 15000 AND 18000
Le prix a généralement une cardinalité plus élevée. Il se peut qu'il n'y ait que quelques dizaines de couleurs disponibles, mais très probablement des milliers de prix différents.
Parmi ces choix d'index, idx01
fournit le chemin le plus rapide pour satisfaire la requête:
CREATE INDEX idx01 ON dbo.inventory (price, color)
CREATE INDEX idx02 ON dbo.inventory (color, price)
En effet, moins de voitures satisferont le prix que le choix de couleur, ce qui donne au moteur de requête beaucoup moins de données à analyser.
Je suis connu pour avoir deux index très similaires ne différant que dans l'ordre des champs pour accélérer les requêtes (prénom, nom) dans l'un et (nom, prénom) dans l'autre.
Une astuce que j'ai récemment apprise est que SQL Server peut mettre à jour des variables locales ainsi que des champs, dans une instruction de mise à jour.
UPDATE table
SET @variable = column = @variable + otherColumn
Ou la version la plus lisible:
UPDATE table
SET
@variable = @variable + otherColumn,
column = @variable
Je l'ai utilisé pour remplacer les curseurs / jointures compliqués lors de la mise en œuvre de calculs récursifs, et j'ai également beaucoup gagné en performances.
Voici des détails et des exemples de code qui ont apporté des améliorations fantastiques aux performances: http://geekswithblogs.net/Rhames/archive/2008/10/28/calculating-running-totals-in-sql-server-2005---the-optimal. aspx
En supposant MySQL ici, utilisez EXPLAIN pour découvrir ce qui se passe avec la requête, assurez-vous que les index sont utilisés aussi efficacement que possible et essayez d'éliminer les types de fichiers. MySQL hautes performances: optimisation, sauvegardes, réplication, etc. est un excellent livre sur ce sujet, tout comme MySQL Performance Blog .
@Terrapin, il y a quelques autres différences entre isnull et coalesce qui méritent d'être mentionnées (en plus de la conformité ANSI, ce qui est important pour moi).
Parfois, dans SQL Server, si vous utilisez un OR dans une clause where, il sera vraiment très performant. Au lieu d'utiliser le OU, faites simplement deux sélections et unissez-les ensemble. Vous obtenez les mêmes résultats à 1000 fois la vitesse.
Je vais généralement commencer par les jointures - je vais supprimer chacune d'elles de la requête une par une et réexécuter la requête pour avoir une idée s'il y a une jointure particulière avec laquelle j'ai un problème.
Sur toutes mes tables temporaires, j'aime ajouter des contraintes uniques (le cas échéant) pour créer des index et des clés primaires (presque toujours).
declare @temp table(
RowID int not null identity(1,1) primary key,
SomeUniqueColumn varchar(25) not null,
SomeNotUniqueColumn varchar(50) null,
unique(SomeUniqueColumn)
)
J'ai pris l'habitude de toujours utiliser des variables de liaison. Il est possible que les variables de liaison n'aident pas si le SGBDR ne met pas en cache les instructions SQL. Mais si vous n'utilisez pas de variables de liaison, le SGBDR n'a pas la possibilité de réutiliser les plans d'exécution de requête et les instructions SQL analysées. Les économies peuvent être énormes: http://www.akadia.com/services/ora_bind_variables.html . Je travaille principalement avec Oracle, mais Microsoft SQL Server fonctionne à peu près de la même manière.
D'après mon expérience, si vous ne savez pas si vous utilisez ou non des variables de liaison, vous ne l'êtes probablement pas. Si la langue de votre application ne les prend pas en charge, trouvez-en une qui le fait. Parfois, vous pouvez corriger la requête A en utilisant des variables de liaison pour la requête B.
Après cela, je parle à notre DBA pour savoir ce qui cause le plus de douleur au SGBDR. Notez que vous ne devriez pas demander "Pourquoi cette requête est-elle lente?" C'est comme demander à votre médecin de vous retirer l'appendice. Bien sûr, votre requête peut être le problème, mais il est tout aussi probable que quelque chose ne va pas. En tant que développeurs, nous avons tendance à penser en termes de lignes de code. Si une ligne est lente, corrigez cette ligne. Mais un SGBDR est un système vraiment compliqué et votre lenteur de requête peut être le symptôme d'un problème beaucoup plus important.
Beaucoup trop de conseils de réglage SQL sont des idoles de culte du fret. La plupart du temps, le problème n'est pas lié ou peu lié à la syntaxe que vous utilisez, il est donc normalement préférable d'utiliser la syntaxe la plus propre possible. Ensuite, vous pouvez commencer à chercher des moyens de régler la base de données (pas la requête). Ne modifiez la syntaxe que lorsque cela échoue.
Comme pour tout réglage des performances, collectez toujours des statistiques significatives. N'utilisez pas l'heure de l'horloge murale à moins que ce ne soit l'expérience utilisateur que vous réglez. Au lieu de cela, regardez des choses comme le temps CPU, les lignes récupérées et les blocs lus sur le disque. Trop souvent, les gens optimisent pour la mauvaise chose.
Exécuter la requête en utilisant WITH (NoLock) est à ma place une opération presque standard. Quiconque est surpris en train d'exécuter des requêtes sur les tables de dizaines de gigaoctets sans qu'il soit retiré et abattu.
Convertissez les requêtes NOT IN en GAUCHE OUTER JOINS si possible. Par exemple, si vous souhaitez rechercher toutes les lignes de Table1 qui ne sont pas utilisées par une clé étrangère dans Table2, vous pouvez le faire:
SELECT *
FROM Table1
WHERE Table1.ID NOT IN (
SELECT Table1ID
FROM Table2)
Mais vous obtenez de bien meilleures performances avec ceci:
SELECT Table1.*
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.ID = Table2.Table1ID
WHERE Table2.ID is null
Pas nécessairement une astuce de performance SQL en soi, mais certainement liée:
Une bonne idée serait d'utiliser memcached dans la mesure du possible car il serait beaucoup plus rapide de simplement récupérer les données précompilées directement depuis la mémoire plutôt que de les récupérer depuis la base de données. Il y a aussi une saveur de MySQL qui a été intégrée à Memcached (tiers).
Assurez-vous que vos longueurs d'index sont aussi petites que possible. Cela permet à la base de données de lire plus de clés à la fois à partir du système de fichiers, accélérant ainsi vos jointures. Je suppose que cela fonctionne avec toutes les bases de données, mais je sais que c'est une recommandation spécifique pour MySQL.
Je recherche:
SET NOCOUNT ON
Habituellement, la première ligne de mes procédures stockées, sauf si j'ai réellement besoin d'utiliser @@ROWCOUNT
.
Dans SQL Server, utilisez la directive nolock. Il permet à la commande select de se terminer sans avoir à attendre - généralement d'autres transactions pour se terminer.
SELECT * FROM Orders (nolock) where UserName = 'momma'
Supprimez les curseurs là où ils ne sont pas nécessaires.
Supprimez les appels de fonction dans Sprocs où beaucoup de lignes appelleront la fonction.
Mon collègue a utilisé des appels de fonction (obtenir lastlogindate à partir de userid comme exemple) pour renvoyer des jeux d'enregistrements très larges.
Chargé de l'optimisation, j'ai remplacé les appels de fonction dans le sproc par le code de la fonction: j'ai eu beaucoup de temps d'exécution de sprocs de> 20 secondes à <1.
J'aime utiliser
isnull(SomeColThatMayBeNull, '')
Plus de
coalesce(SomeColThatMayBeNull, '')
Quand je n'ai pas besoin du support d'arguments multiples que vous offre coalesce.
http://blog.falafel.com/2006/04/05/SQLServerArcanaISNULLVsCOALESCE.aspx
Ne préfixez pas les noms de procédure stockée par "sp_" car les procédures système commencent toutes par "sp_" et SQL Server devra rechercher plus dur pour trouver votre procédure lorsqu'elle sera appelée.
Sale lit -
set transaction isolation level read uncommitted
Empêche les verrous morts là où l'intégrité transactionnelle n'est pas absolument nécessaire (ce qui est généralement vrai)
Je vais toujours d'abord à SQL Profiler (s'il s'agit d'une procédure stockée avec beaucoup de niveaux d'imbrication) ou au planificateur d'exécution de requêtes (s'il s'agit de quelques instructions SQL sans imbrication). 90% du temps, vous pouvez trouver le problème immédiatement avec l'un de ces deux outils.