web-dev-qa-db-fra.com

La «table vide» de SQL Server est lente après la suppression de tous les (12 millions) enregistrements?

J'ai une instance SQL Server 2008 avec environ 150 colonnes. J'ai déjà rempli ce tableau avec environ 12 millions d'entrées, mais j'ai depuis effacé le tableau en préparation d'un nouvel ensemble de données.

Cependant, les commandes qui s'exécutaient une fois instantanément sur une table vide, telles que count(*) et select top 1000 dans SQL Management Studio il faut maintenant des éons pour courir.

SELECT COUNT(*) FROM TABLE_NAME 

a mis plus de 11 minutes pour renvoyer 0, et SELECT TOP 1000 a mis près de 10 minutes pour renvoyer une table vide.

J'ai également remarqué que l'espace libre sur mon disque dur a littéralement disparu (environ 100G à 20G). La seule chose qui s'est produite entre les deux est une seule requête que j'ai exécutée:

DELETE FROM TABLE_NAME

Que se passe-t-il dans le monde?!?

27
TheDramaLlama

On vous a déjà expliqué pourquoi TRUNCATE serait tellement plus rapide/meilleur/plus sexy que DELETE, mais il reste une question à résoudre:

Pourquoi SELECT est-il plus lent après DELETE terminé ?

C'est parce que DELETE n'a que fantôme les lignes. Le tableau est tout aussi grand que lorsqu'il avait 12 millions de lignes, même s'il n'en a pas. Pour compter les lignes (0), il faut autant de temps qu'il a fallu pour compter 12 millions de lignes. Avec le temps, le processus de nettoyage des fantômes va récupérer ces enregistrements fantômes et désallouer les pages qui contenaient uniquement les fantômes, et vos SELECTs accéléreront. Mais en ce moment, si vous cochez Skipped Ghosted Records/sec dans perfmon monte probablement en flèche pendant SELECT COUNT(*). Vous pouvez également accélérer les choses en reconstruisant la table: ALTER TABLE ... REBUILD.

TRUNCATE aurait également réglé ce problème, car il ne laisse aucun fantôme derrière lui.

Voir aussi Inside the Storage Engine: Ghost cleanup in depth .

42
Remus Rusanu

Les instructions DELETE suppriment les lignes d'une table une par une, enregistrant chaque ligne dans transaction log, ainsi que la conservation des informations log sequence number (LSN). Puisque vous avez mentionné que votre table contenait d'énormes données (12 millions d'enregistrements), après la suppression de laquelle votre disque dur est à court d'espace, vérifiez la taille de votre fichier journal de base de données. Il aurait très probablement grandi.

une meilleure façon aurait été:

TRUNCATE TABLE_NAME
13
Akhil

(C'était à l'origine un commentaire à la réponse de @ DaveE, mais je l'ai mis dans sa propre réponse car elle est longue)

TRUNCATE est une opération enregistrée. Il doit en être autrement, il n'est pas conforme à ACID. Cependant, les différences entre TRUNCATE et DELETE:

  • Utilisation de l'espace de journalisation: TRUNCATE enregistre uniquement les pages/extensions * libérées, tandis que DELETE enregistre les lignes individuelles.
  • Utilisation des verrous: TRUNCATE utilisera généralement moins de verrous, car il faut un verrou de table et des verrous de page, contrairement à DELETE qui utilise des verrous de ligne **.
  • IDENTITY séquences: TRUNCATE réinitialise la séquence d'identité sur une table, le cas échéant.

(* Une extension = 8 pages. TRUNCATE enregistrera/supprimera les extensions si elles sont toutes de cette même table, sinon elle enregistrera/supprimera les pages des extensions mixtes.

** Un effet secondaire de ceci est que DELETE FROM TABLE peut potentiellement laisser des pages vides allouées à la table, selon que l'opération peut ou non obtenir un verrou de table exclusif.)

Revenons donc à la question initiale), TRUNCATE TABLE est nettement meilleur que DELETE FROM TABLE si vous videz la table mais que vous souhaitez conserver la structure (NB: TRUNCATE ne peut pas être utilisé sur une table référencée par une clé étrangère d'une autre table).

Comme indiqué dans le commentaire de @ Tullo, vérifiez également le modèle de récupération de votre base de données - s'il est plein, vous devez soit commencer à prendre des sauvegardes de journaux, soit changer votre modèle de récupération en simple. Une fois que vous avez fait l'un ou l'autre, vous voudrez probablement réduire votre fichier journal comme une opération unique (NB: fichier journal - seulement) afin de récupérer tout cet espace libre.

Enfin, une autre chose à savoir - les statistiques de table. courir UPDATE STATISTICS <TABLENAME>' afterTRONQUER/DELETE` pour que l'optimiseur de requêtes ne soit pas gâché par les anciennes statistiques.

3
Simon Righarts

(REMARQUE: je ne suis pas un DBA) DELETE est une opération enregistrée et ne libère pas l'espace utilisé. Vous disposez probablement d'un grand journal des transactions occupant de l'espace et des analyses de table s'exécutant sur l'espace table "vide". Je suppose que vous devez effacer le journal des transactions et réduire votre base de données. Cet article StackOverflow devrait vous aider à démarrer.

Et utilisez TRUNCATE TABLE lorsque vous souhaitez le faire à l'avenir.

EDIT: Ma déclaration sur TRUNCATE non connecté était en erreur. supprimé.

2
DaveE