Astuces de réglage des performances préférées [fermé]


126

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?



Je conviens que ce n'est pas constructif et peut être recherché dans Google, mais pourquoi il a 118 uv?! :)
FLICKER

Réponses:


114

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.

Liste de contrôle de l'optimisation des requêtes

  • Exécutez UPDATE STATISTICS sur les tables sous-jacentes
    • De nombreux systèmes exécutent cela comme un travail hebdomadaire planifié
  • Supprimer les enregistrements des tables sous-jacentes (éventuellement archiver les enregistrements supprimés)
    • Pensez à le faire automatiquement une fois par jour ou une fois par semaine.
  • Reconstruire les index
  • Reconstruire les tables (sortie / entrée de données bcp)
  • Dump / Recharger la base de données (drastique, mais pourrait corriger la corruption)
  • Créer un nouvel index plus approprié
  • Exécutez DBCC pour voir s'il y a une corruption possible dans la base de données
  • Serrures / Deadlocks
    • Assurez-vous qu'aucun autre processus ne s'exécute dans la base de données
      • Surtout DBCC
    • Utilisez-vous le verrouillage au niveau des lignes ou des pages?
    • Verrouillez les tables exclusivement avant de lancer la requête
    • Vérifiez que tous les processus accèdent aux tables dans le même ordre
  • Les indices sont-ils utilisés de manière appropriée?
    • Les jointures n'utiliseront l'index que si les deux expressions sont exactement du même type de données
    • L'index ne sera utilisé que si le ou les premiers champs de l'index correspondent à la requête
    • Des indices groupés sont-ils utilisés le cas échéant?
      • données de plage
      • Champ WHERE entre valeur1 et valeur2
  • Les petites jointures sont de belles jointures
    • Par défaut, l'optimiseur ne considérera que les tables 4 à la fois.
    • Cela signifie que dans les jointures avec plus de 4 tables, il a de bonnes chances de choisir un plan de requête non optimal
  • Rompre la jointure
    • Pouvez-vous rompre la jointure?
    • Présélectionnez les clés étrangères dans une table temporaire
    • Faites la moitié de la jointure et placez les résultats dans une table temporaire
  • Utilisez-vous le bon type de table temporaire?
    • #temples tables peuvent être bien meilleures que les @tablevariables avec de gros volumes (des milliers de lignes).
  • Tenir à jour les tableaux récapitulatifs
    • Construire avec des déclencheurs sur les tables sous-jacentes
    • Construire quotidiennement / toutes les heures / etc.
    • Construire ad-hoc
    • Construire de manière incrémentielle ou démonter / reconstruire
  • Découvrez le plan de requête avec SET SHOWPLAN ON
  • Découvrez ce qui se passe réellement avec SET STATS IO ON
  • Forcer un index en utilisant le pragma: (index: myindex)
  • Forcer l'ordre des tables à l'aide de SET FORCEPLAN ON
  • Reniflage des paramètres:
    • Diviser la procédure stockée en 2
    • appeler proc2 depuis proc1
    • permet à l'optimiseur de choisir l'index dans proc2 si @parameter a été modifié par proc1
  • Pouvez-vous améliorer votre matériel?
  • A quelle heure courez-vous? Y a-t-il un moment plus calme?
  • Replication Server (ou un autre processus non-stop) est-il en cours d’exécution? Pouvez-vous le suspendre? Exécutez-le par exemple. toutes les heures?

2
à quel élément parlez-vous?
AJ.

2
Ce sont des trucs sympas, mais j'aimerais que vous ayez des références pour certaines revendications. Par exemple: je n'avais jamais entendu que l'optimisation ne considère que 4 tables à la fois dans une jointure. Je ne comprends pas comment cela pourrait être juste. Pourriez-vous fournir des références à ce sujet en particulier? J'adorerais voir où vous obtenez cela.
SheldonH

19
  1. Ayez une assez bonne idée du chemin optimal pour exécuter la requête dans votre tête.
  2. Vérifiez le plan de requête - toujours.
  3. Activez STATS pour pouvoir examiner les performances d'E / S et du processeur. Concentrez-vous sur la réduction de ces chiffres, pas nécessairement sur le temps de requête (car cela peut être influencé par une autre activité, un cache, etc.).
  4. Recherchez un grand nombre de lignes entrant dans un opérateur, mais de petits nombres sortant. Habituellement, un index aiderait en limitant le nombre de lignes entrantes (ce qui enregistre les lectures de disque).
  5. Concentrez-vous d'abord sur le sous-arbre de coût le plus élevé. La modification de cette sous-arborescence peut souvent modifier l'ensemble du plan de requête.
  6. Les problèmes courants que j'ai vus sont:
    • S'il y a beaucoup de jointures, Sql Server choisira parfois d'étendre les jointures, puis appliquera les clauses WHERE. Vous pouvez généralement résoudre ce problème en déplaçant les conditions WHERE dans la clause JOIN ou dans une table dérivée avec les conditions en ligne. Les vues peuvent causer les mêmes problèmes.
    • Jointures sous-optimales (LOOP vs HASH vs MERGE). Ma règle de base est d'utiliser une jointure LOOP lorsque la ligne du haut a très peu de lignes par rapport au bas, une fusion lorsque les ensembles sont à peu près égaux et ordonnés, et un HASH pour tout le reste. L'ajout d'un indice de jointure vous permettra de tester votre théorie.
    • Reniflage de paramètres. Si vous exécutez le processus stocké avec des valeurs irréalistes au début (par exemple, pour le test), le plan de requête mis en cache peut être sous-optimal pour vos valeurs de production. Une nouvelle exécution avec RECOMPILE devrait vérifier cela. Pour certains processus stockés, en particulier ceux qui traitent des plages de tailles variables (par exemple, toutes les dates entre aujourd'hui et hier - ce qui impliquerait une recherche d'INDEX - ou, toutes les dates entre l'année dernière et cette année - ce qui serait mieux avec un INDEX SCAN ), vous devrez peut-être l'exécuter AVEC RECOMPILE à chaque fois.
    • Mauvaise indentation ... D'accord, donc Sql Server n'a pas de problème avec cela - mais je trouve certainement qu'il est impossible de comprendre une requête avant d'avoir corrigé le formatage.

1
+1 pour l'inclusion d'une mauvaise indentation. Le formatage est la clé! :)
mwigdahl

18

Un peu hors sujet mais si vous avez le contrôle sur ces problèmes ...
Haut niveau et fort impact.

  • Pour les environnements à IO élevés, assurez-vous que vos disques sont pour RAID 10 ou RAID 0 + 1 ou pour une implémentation imbriquée de raid 1 et raid 0.
  • N'utilisez pas de lecteurs de moins de 1500K.
  • Assurez-vous que vos disques ne sont utilisés que pour votre base de données. IE pas de journalisation pas de système d'exploitation.
  • Désactivez la croissance automatique ou une fonctionnalité similaire. Laissez la base de données utiliser tout le stockage prévu. Pas nécessairement ce qui est actuellement utilisé.
  • concevez votre schéma et vos index pour les requêtes de type.
  • s'il s'agit d'une table de type journal (insertion uniquement) et doit être dans la base de données, ne l'indexez pas.
  • si vous faites beaucoup de rapports (sélections complexes avec de nombreuses jointures), vous devriez envisager de créer un entrepôt de données avec un schéma en étoile ou en flocon de neige.
  • N'ayez pas peur de répliquer les données en échange de performances!

8

CREATE INDEX

Assurez-vous qu'il existe des index disponibles pour vos clauses WHEREet 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, idx01fournit 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.


6

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


5

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 .


3
C'est bien pour MySQL, mais la question a été étiquetée "sqlserver". Pourtant, c'est une bonne chose de faire cela. La chose analogue à faire dans SSMS est d'utiliser "Afficher le plan d'exécution estimé" et "Inclure le plan d'exécution réel". Si vous pouvez éliminer les analyses de table volumineuses et utiliser des recherches d'index en cluster, vous êtes sur la bonne voie pour des performances optimales.
eksortso

5

@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).

Coalesce contre IsNull


3

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.


J'ai vu ce comportement inexpliqué.
Esen

2

Regardez la clause where - vérifiez l'utilisation des index / vérifiez que rien de stupide n'est fait

where SomeComplicatedFunctionOf(table.Column) = @param --silly

2

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.


2

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)
)

2

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.


2

Première étape: regardez le plan d'exécution des requêtes!
TableScan -> bad
NestedLoop -> meh warning
TableScan derrière un NestedLoop -> DOOM!

SET STATISTICS IO ON
SET STATISTICS TIME ON


2

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.


2
Cela devrait être utilisé judicieusement, pas habituellement. Le verrouillage n'est pas un mal, il est juste mal compris.

2

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

1

@ DavidM

En supposant MySQL ici, utilisez EXPLAIN pour savoir ce qui se passe avec la requête, assurez-vous que les index sont utilisés le plus efficacement possible ...

Dans SQL Server, le plan d'exécution vous donne la même chose - il vous indique quels index sont touchés, etc.


1

Indexez la (les) table (s) en fonction des clm sur lesquelles vous filtrez


1

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).


1

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.


1

Je recherche:

  • Déroulez toutes les boucles CURSOR et convertissez-les en instructions UPDATE / INSERT basées sur un ensemble.
  • Recherchez tout code d'application qui:
    • Appelle un SP qui renvoie un grand nombre d'enregistrements,
    • Ensuite, dans l'application, parcourt chaque enregistrement et appelle un SP avec des paramètres pour mettre à jour les enregistrements.
    • Convertissez-le en un SP qui effectue tout le travail en une seule transaction.
  • Tout SP qui fait beaucoup de manipulation de chaînes. C'est la preuve que les données ne sont pas structurées correctement / normalisées.
  • Tous les SP qui réinventent la roue.
  • Tous les SP que je ne peux pas comprendre ce qu'il essaie de faire en une minute!

1
SET NOCOUNT ON

Habituellement, la première ligne de mes procédures stockées, sauf si j'ai réellement besoin d'utiliser @@ROWCOUNT.


2
@@ ROWCOUNT est défini de toute façon. NOCOUNT désactive les instructions "xx lignes affectées".
Sklivvz

Cela fait-il jamais vraiment une différence appréciable dans les performances?
JohnFx

Ouais, alors le nombre n'est pas calculé automatiquement chaque fois qu'une instruction SQL est exécutée. Il est assez facile de comparer une requête avec et sans pour voir que cela fait une différence.
travis

Le décompte est de toute façon suivi dans SQL Server. Toute différence de performance que vous voyez est due au fait que les décomptes doivent passer par le réseau jusqu'à votre front-end. Si vous faites un seul SELECT, cela ne fera pas de différence appréciable. Si vous avez une boucle avec 100000 inserts, c'est beaucoup plus sur le réseau.
Tom H

1

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'

3
NOLOCK est uniquement pour les requêtes pour lesquelles vous ne vous souciez pas des résultats corrects
Mark Sowul

1

Supprimez les curseurs là où ils ne sont pas nécessaires.


Ouais, les curseurs sont une malédiction! ;)
Sklivvz

8
Pouah. Ne jetez pas ça sans réserve comme ça. Les curseurs sont comme des armes à feu. Ils ne sont pas mauvais en eux-mêmes, c'est juste que les gens font de très mauvaises choses avec eux.
JohnFx

1

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.


0
  • Préfixez toutes les tables avec dbo. pour empêcher les recompilations.
  • Affichez les plans de requête et recherchez des analyses de table / index.
  • En 2005, parcourez les vues de gestion à la recherche d'index manquants.


0

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.


1
Avez-vous réellement évalué celui-ci? Si SQL Server fait ce qui est raisonnable (en utilisant un algorithme de hachage pour localiser le processus stocké), cela ne ferait aucune différence. En fait, si SQL Server ne faisait pas cela, il semble que les performances du système pue (car il appelle vraisemblablement ses propres processus).
John Stauffer le

1
Je pense que cela tombe dans le seau de l'optimisation prématurée. C'est probablement une bonne pratique pour éviter la confusion pour les gens, mais comme astuce d'optimisation ... D-
JohnFx

0

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)


1
Oui, mais cela peut conduire à des bugs étranges qui sont TRÈS difficiles à trouver.
Grant Johnson

0

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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.