web-dev-qa-db-fra.com

Requêtes et mises à jour extrêmement lentes après IndexOptimize

Base de données SQL Server 2017 Enterprise CU16 14.0.3076.1

Nous avons récemment essayé de passer des tâches de maintenance par défaut de reconstruction d'index à Ola Hallengren IndexOptimize. Les travaux de reconstruction d'index par défaut étaient en cours d'exécution depuis quelques mois sans aucun problème, et les requêtes et les mises à jour fonctionnaient avec des délais d'exécution acceptables. Après avoir exécuté IndexOptimize sur la base de données:

EXECUTE dbo.IndexOptimize
@Databases = 'USER_DATABASES',
@FragmentationLow = NULL,
@FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE',
@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE',
@FragmentationLevel1 = 5,
@FragmentationLevel2 = 30,
@UpdateStatistics = 'ALL',
@OnlyModifiedStatistics = 'Y'

les performances étaient extrêmement dégradées. Une instruction de mise à jour qui prenait 100 ms avant IndexOptimize prenait 78 000 ms par la suite (en utilisant un plan identique), et les requêtes exécutaient également plusieurs ordres de grandeur.

Comme il s'agit toujours d'une base de données de test (nous migrons un système de production depuis Oracle), nous sommes revenus à une sauvegarde et avons désactivé IndexOptimize et tout est revenu à la normale.

Cependant, nous aimerions comprendre ce que IndexOptimize fait différemment du _ "normal" Index Rebuild qui aurait pu provoquer cette dégradation extrême des performances afin de nous assurer de l'éviter une fois en production. Toute suggestion sur ce qu'il faut rechercher serait grandement appréciée.

Plan d'exécution de l'instruction de mise à jour lorsqu'elle est lente. c'est à dire.
après IndexOptimize
Plan d'exécution réel (à venir dès que possible)

Je n'ai pas pu voir de différence.
Planifiez la même requête lorsqu'elle est rapide
Plan d'exécution réel

12
Martin Bergström

Je suppose que vous avez un taux d'échantillonnage différent défini entre vos deux approches de maintenance. Je pense que les scripts d'Ola utilisent l'échantillonnage par défaut, sauf si vous spécifiez le @StatisticsSample paramètre , ce qui ne semble pas être le cas actuellement.

À ce stade, il s'agit de spéculations, mais vous pouvez vérifier le taux d'échantillonnage actuellement utilisé dans vos statistiques en exécutant la requête suivante dans votre base de données:

SELECT  OBJECT_SCHEMA_NAME(st.object_id) + '.' + OBJECT_NAME(st.object_id) AS TableName
    ,   col.name AS ColumnName
    ,   st.name AS StatsName
    ,   sp.last_updated
    ,   sp.rows_sampled
    ,   sp.rows
    ,   (1.0*sp.rows_sampled)/(1.0*sp.rows) AS sample_pct
FROM sys.stats st 
    INNER JOIN sys.stats_columns st_col
        ON st.object_id = st_col.object_id
        AND st.stats_id = st_col.stats_id
    INNER JOIN sys.columns col
        ON st_col.object_id = col.object_id
        AND st_col.column_id = col.column_id
    CROSS APPLY sys.dm_db_stats_properties (st.object_id, st.stats_id) sp
ORDER BY 1, 2

Si vous voyez que cela passe par un 1 (par exemple 100%), c'est probablement votre problème. Essayez peut-être à nouveau les scripts d'Ola, y compris le @StatisticsSample paramètre avec le pourcentage renvoyé par cette requête et voir si cela résout votre problème?


Comme preuve supplémentaire à l'appui de cette théorie, le plan d'exécution XML montre des taux d'échantillonnage très différents pour la requête lente (2,18233%):

<StatisticsInfo LastUpdate="2019-09-01T01:07:46.04" ModificationCount="0" 
    SamplingPercent="2.18233" Statistics="[INDX_UPP_4]" Table="[UPPDRAG]" 
    Schema="[SVALA]" Database="[ulek-sva]" />

Contre la requête rapide (100%):

<StatisticsInfo LastUpdate="2019-08-25T23:01:05.52" ModificationCount="555" 
    SamplingPercent="100" Statistics="[INDX_UPP_4]" Table="[UPPDRAG]" 
    Schema="[SVALA]" Database="[ulek-sva]" />
11
John Eisbrener

la réponse de John est la bonne solution, ce n'est qu'un ajout sur les parties du plan d'exécution modifiées et un exemple sur la façon de repérer facilement les différences avec Sentry One Plan Explorer

Une déclaration de mise à jour qui a pris 100 ms avant que IndexOptimize prenne 78 000 ms par la suite (en utilisant un plan identique)

Lorsque vous regardez tous les plans de requête lorsque vos performances ont été dégradées, vous pouvez facilement repérer les différences.

Performances dégradées

enter image description here

Deux comptes de plus de 35 secondes de temps processeur et de temps écoulé

Performances attendues

enter image description here

Beaucoup mieux

La dégradation principale est deux fois sur cette requête de mise à jour:

UPDATE SVALA.INGÅENDEANALYS
                           SET 
                              UPPDRAGAVSLUTAT = @NEW$AVSLUTAT
                        WHERE INGÅENDEANALYS.ID IN 
                           (
                              SELECT IA.ID
                              FROM 
                                 SVALA.INGÅENDEANALYS  AS IA 
                                    JOIN SVALA.INGÅENDEANALYSX  AS IAX 
                                    ON IAX.INGÅENDEANALYS = IA.ID 
                                    JOIN SVALA.ANALYSMATERIAL  AS AM 
                                    ON AM.ID = IA.ANALYSMATERIALID 
                                    JOIN SVALA.ANALYSMATERIALX  AS AMX 
                                    ON AMX.ANALYSMATERIAL = AM.ID 
                                    JOIN SVALA.INSÄNTMATERIAL  AS IM 
                                    ON IM.ID = AM.INSÄNTMATERIALID 
                                    JOIN SVALA.INSÄNTMATERIALX  AS IMX 
                                    ON IMX.INSÄNTMATERIAL = IM.ID
                              WHERE IM.UPPDRAGSID = SVALA.PKGSVALA$STRIPVERSION(@NEW$ID)
                      )

le plan d'exécution de cette requête avec des performances dégradées

Le plan de requête estimé de cette requête de mise à jour a des estimations très élevées lorsque les performances ont été dégradées:

enter image description here

Alors qu'en réalité (le plan d'exécution réel), il doit encore faire du travail, mais pas le montant fou que les estimations montrent.

Le plus grand impact sur les performances est les deux scans et correspondances de hachage ci-dessous:

Analyse réelle des performances dégradées # 1

enter image description here

Analyse réelle des performances dégradées # 2

enter image description here


Le plan d'exécution de cette requête avec les performances attendues

Lorsque vous comparez cela aux estimations (ou chiffres réels) du plan de requête avec des performances attendues normales, les différences sont faciles à repérer.

enter image description here

De plus, les deux accès précédents aux tables ne se sont même pas produits:

enter image description here

enter image description here

enter image description here

Vous ne voyez pas cette élimination sur la jointure de hachage car l'entrée de construction (en haut) est insérée en premier dans la table de hachage. Ensuite, des valeurs nulles sont sondées dans cette table de hachage, retournant des valeurs nulles.

5
Randi Vertongen

Sans plus d'informations, nous ne pouvons que prendre des coups de couteau légèrement informés dans l'obscurité, vous devez donc modifier la question pour en fournir un peu plus. Par exemple, les plans de requête pour cette instruction de mise à jour pour laquelle vous avez donné les délais, à la fois avant et après les opérations de maintenance d'index, car les plans peuvent différer en raison de la mise à jour des statistiques d'index ( https: //www.brentozar .com/pastetheplan / est utile pour cela, plutôt que de remplir la question avec ce qui pourrait être un énorme morceau de XML ou de donner une capture d'écran qui n'inclut pas certaines des informations pertinentes contenues dans le texte du plan) .

Cependant, deux points très simples:

  1. Le cycle d'optimisation est-il définitivement terminé? Si vos tests sont en concurrence avec le IO de reconstructions d'index de longue durée qui affecteront les timings.
  2. Avez-vous testé plusieurs fois? Si la mise à jour est basée sur les données d'une requête qui prend en compte un grand nombre de données (plutôt qu'un simple `UPDATE TheTable SET ThisColumn = 'A Static Value'), il se peut que ces données soient normalement en mémoire mais ont été éliminées dans dont les premières exécutions de requêtes connexes seront plus lentes que d'habitude en raison du fait de frapper le disque plutôt que de trouver les pages nécessaires déjà dans le pool de mémoire tampon en mémoire.
1
David Spillett