web-dev-qa-db-fra.com

SELECT TOP 1 à partir d'une très grande table sur une colonne d'index est très lent, mais pas dans l'ordre inverse ("desc")

Nous avons une grande base de données, environ 1 To, exécutant SQL Server 2014 sur un serveur puissant. Tout a bien fonctionné pendant quelques années. Il y a environ 2 semaines, nous avons effectué une maintenance complète, qui comprenait: installer toutes les mises à jour logicielles; reconstruisez tous les index et fichiers DB compacts. Cependant, nous ne nous attendions pas à ce qu'à un certain stade, l'utilisation du processeur de la base de données augmente de plus de 100% à 150% lorsque la charge réelle était la même.

Après beaucoup de dépannage, nous l'avons réduit à une requête très simple, mais nous n'avons pas pu trouver de solution. La requête est extrêmement simple:

select top 1 EventID from EventLog with (nolock) order by EventID

Cela prend toujours environ 1,5 seconde! Cependant, une requête similaire avec "desc" prend toujours environ 0 ms:

select top 1 EventID from EventLog with (nolock) order by EventID desc

PTable compte environ 500 millions de lignes; EventID est la colonne d'index cluster principal (ordonnée ASC) avec le type de données bigint (colonne Identity). Il y a plusieurs threads insérant des données dans la table en haut (plus gros EventID), et il y a 1 thread qui supprime les données du bas (plus petits EventID).

Dans SMSS, nous avons vérifié que les deux requêtes utilisent toujours le même plan d'exécution:

  • Analyse d'index en cluster;

  • Les nombres de lignes estimés et réels sont tous deux 1;

  • Le nombre estimé et réel d'exécutions est de 1;

  • Le coût d'E/S estimé est de 8500 (semble élevé)

  • S'il est exécuté consécutivement, le coût de la requête est le même de 50% pour les deux.

J'ai mis à jour les statistiques d'index with fullscan, le problème a persisté; J'ai reconstruit l'indice à nouveau, et le problème semblait avoir disparu pendant une demi-journée, mais est revenu.

J'ai activé IO statistiques avec:

set statistics io on

puis a exécuté les deux requêtes consécutivement et a trouvé les informations suivantes:

(Pour la première requête, la lente)

Tableau "PTable". Nombre de balayages 1, lectures logiques 407670, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0.

(Pour la deuxième requête, la rapide)

Tableau "PTable". Nombre de balayages 1, lectures logiques 4, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0.

Notez l'énorme différence dans les lectures logiques. L'index est utilisé dans les deux cas.

La fragmentation de l'indice pourrait expliquer un peu, mais je pense que l'impact est très faible; et le problème ne s'est jamais produit auparavant. Une autre preuve est que si je lance une requête comme:

select * from EventLog with (nolock) where EventID=xxxx   

Même si j'ai défini xxxx sur les plus petits EventID de la table, la requête est toujours rapide comme l'éclair.

Nous avons vérifié et il n'y a aucun problème de verrouillage/blocage.

Remarque: J'ai juste essayé de simplifier le problème ci-dessus. Le "PTable" est en fait "EventLog"; PID est EventID.

J'obtiens le même résultat test sans l'indication NOLOCK.

Quelqu'un peut-il aider?

enter image description here

enter image description here

Plans d'exécution de requête plus détaillés en XML comme suit:

https://www.brentozar.com/pastetheplan/?id=SJ3eiVnob

https://www.brentozar.com/pastetheplan/?id=r1rOjVhoZ

Je ne pense pas qu'il soit important de fournir l'instruction create table. Il s'agit d'une ancienne base de données qui fonctionne parfaitement bien depuis longtemps jusqu'à la maintenance. Nous avons fait beaucoup de recherches nous-mêmes et l'avons réduit aux informations fournies dans ma question.

La table a été créée normalement avec la colonne EventID comme clé primaire, qui est une colonne identity de type bigint. Pour l'instant, je suppose que le problème vient de la fragmentation de l'index. Juste après la reconstruction de l'index, le problème semblait avoir disparu pendant une demi-journée; mais pourquoi il est revenu si vite ...?

17
TiffanyP

Le balayage d'index clusterisé montre 423 723 lectures logiques pour retourner la première ligne, en prenant 1926 ms:

NUTS

Cela semble plutôt difficile de localiser la première ligne dans l'ordre des index.

Votre tâche de nettoyage des fantômes est très probablement en retard ou s'est arrêtée. Vous devriez vérifier le ghost_record_count pour l'index cluster dans sys.dm_db_index_physical_stats et surveiller les changements au fil du temps.

Le scan ordonné à la fin de l'index qui voit une activité de suppression constante doit parcourir un tas d'enregistrements fantômes avant de trouver le premier "vivant". 'ligne pour revenir. Cela explique les lectures logiques supplémentaires. Une recherche dans l'arbre b jusqu'à la valeur la plus basse de l'index rencontrera beaucoup moins d'enregistrements fantômes.

Un autre facteur affectant les performances est que l'analyse elle-même devient responsable de la suppression des enregistrements fantômes comme mentionné dans Inside the Storage Engine: Ghost cleanup in depth par Paul Randal.

Vous devez vérifier que l'indicateur de trace 661 (désactiver le nettoyage des fantômes) n'est pas actif.

Solutions

Si le processus de nettoyage des fantômes s'est complètement arrêté, la solution la plus efficace consiste normalement à redémarrer l'instance SQL Server. Vous devez également vous assurer que SQL Server exécute l'une des dernières mises à jour cumulatives. Il y a eu de nombreux bugs de nettoyage des fantômes au fil des ans.

Dans votre cas particulier:

Il s'est avéré que le problème était dû à une autre base de données de test sur le même serveur. Cette base de données de test a été restaurée avec une "perte de données" et est corrompue. Étonnamment, le processus de nettoyage des fantômes était apparemment bloqué dans cette base de données. Une fois que nous avons supprimé cette base de données corrompue de SMSS, le problème a été résolu par lui-même (cela a pris du temps et pourrait avoir provoqué un blocage de la base de données pendant un court moment).

18
Paul White 9