Il existe un mythe persistant dans le monde SQL Server selon lequel à la fois DROP TABLE
et TRUNCATE TABLE
les commandes ne sont pas enregistrées.
Ils ne sont pas. Ils sont tous les deux entièrement connectés, mais efficacement.
Vous pouvez facilement vous le prouver. Exécutez le code suivant pour configurer une base de données et une table de test, et montrez que nous avons 10 000 lignes dans notre table :
CREATE DATABASE [TruncateTest]; GO ALTER DATABASE [TruncateTest] SET RECOVERY SIMPLE; GO USE [TruncateTest]; GO CREATE TABLE [TestTable] ( [c1] INT IDENTITY, [c2] CHAR (8000) DEFAULT 'a'); GO SET NOCOUNT ON; GO INSERT INTO [TestTable] DEFAULT VALUES; GO 10000 SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Résultats :
RowCount———–
10000
Et puis le code suivant, qui tronque la table dans une transaction et vérifie le nombre de lignes :
BEGIN TRAN; GO TRUNCATE TABLE [TestTable]; GO SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Résultats :
RowCount———–
0
Maintenant le tableau est vide. Mais je peux annuler la transaction et remettre toutes les données :
ROLLBACK TRAN; GO SELECT COUNT (*) AS N'RowCount' FROM [TestTable]; GO
Résultats :
RowCount———–
10000
Clairement le TRUNCATE
l'opération doit être enregistrée sinon l'opération de restauration ne fonctionnerait pas.
Alors d'où vient cette idée fausse ?
Cela vient du comportement de DROP
et TRUNCATE
opérations sur de grandes tables. Ils se termineront presque instantanément, et si vous regardez dans le journal des transactions en utilisant fn_dblog
juste après, vous ne verrez qu'un petit nombre d'enregistrements de journal générés à partir de l'opération. Ce petit nombre n'est pas en corrélation avec la taille de la table tronquée ou supprimée, il semble donc que DROP
et TRUNCATE
les opérations ne sont pas enregistrées.
Mais ils sont entièrement connectés, comme je l'ai démontré ci-dessus. Alors, où sont les enregistrements du journal des opérations ?
La réponse est que les enregistrements de journal seront créés, mais pas immédiatement, par un mécanisme appelé « suppression différée », qui a été ajouté dans SQL Server 2000 SP3.
Lorsqu'une table est supprimée ou tronquée, toutes les pages du fichier de données allouées à la table doivent être désallouées. Le mécanisme pour cela avant SQL Server 2000 SP3 était le suivant :
Pour chaque extent alloué à la tableCommencer
Acquérir un verrou d'allocation eXclusive sur l'extent
Sonder le verrouiller la page pour chaque page de l'étendue (acquérir le verrou en mode eXclusif et le supprimer immédiatement, en s'assurant que personne d'autre n'a verrouillé la page)
NE PAS libère le verrou d'étendue, garantissant que personne d'autre ne peut utiliser cette étendue
Passer à l'étendue suivante
Fin
Comme tous les verrous d'étendue étaient maintenus jusqu'à la fin de l'opération et que chaque verrou occupe une petite quantité de mémoire, il était possible que le gestionnaire de verrous manque de mémoire lorsqu'un DROP
ou TRUNCATE
d'une très grande table s'est produite. Certains clients SQL Server ont commencé à constater qu'ils rencontraient des problèmes de mémoire insuffisante sur SQL Server 2000, car les tables devenaient très volumineuses et dépassaient largement la croissance de la mémoire système.
Le mécanisme de dépôt différé simule le DROP
ou TRUNCATE
opération se terminant immédiatement, en décrochant les allocations pour la table et en les plaçant dans la "file d'attente de suppression différée", pour un traitement ultérieur par une tâche en arrière-plan. Cette opération de décrochage et de transfert ne génère qu'une poignée d'enregistrements de journal. C'est l'opération qui est effectuée et annulée dans mon exemple de code ci-dessus.
La "tâche d'arrière-plan de dépôt différé" tourne toutes les quelques secondes et libère toutes les pages et extensions de la file d'attente de dépôt différé en petits lots, garantissant que l'opération ne manquera pas de mémoire. Ces désallocations sont toutes entièrement consignées, mais rappelez-vous que la désallocation d'une page pleine de données ou d'enregistrements d'index ne consigne pas les suppressions individuelles de ces enregistrements ; au lieu de cela, la page entière est simplement marquée comme désallouée dans la carte d'octets d'allocation PFS (Page Free Space) appropriée.
À partir de SQL Server 2000 SP3, lorsque vous effectuez un DROP
ou TRUNCATE
d'une table, vous ne verrez que quelques enregistrements de journal générés. Si vous attendez environ une minute, puis regardez à nouveau dans le journal des transactions, vous verrez que des milliers d'enregistrements de journal ont été générés par l'opération de suppression différée, chacun désallouant une page ou une étendue. L'opération est entièrement et efficacement enregistrée.
Voici un exemple, utilisant le scénario que nous avons créé ci-dessus :
CHECKPOINT; GO TRUNCATE TABLE [TestTable]; GO SELECT COUNT (*) AS N'LogRecCount' FROM fn_dblog (NULL, NULL); GO
Résultats :
LogRecCount———–
25
Comme vous pouvez le voir, il n'y a clairement pas d'enregistrements de journal désallouant les 10 000 pages, plus 1 250 extensions dans la table TestTable.
Si j'attends quelques secondes, puis lance le fn_dblog
code à nouveau, j'obtiens :
———–
3811
Vous vous demandez peut-être pourquoi il n'y a pas au moins 10 000 enregistrements de journal - un pour chaque page désallouée. En effet, les désallocations de pages sont même enregistrées efficacement - avec un enregistrement de journal reflétant les changements d'allocation de page PFS pour 8 pages de fichier de données consécutives, au lieu d'un enregistrement de journal pour chaque page de fichier de données reflétant son statut d'allocation changeant dans la page PFS.
SQL Server essaie toujours de produire le moins de journaux de transactions possible, tout en respectant les règles de journalisation complète ou minimale en fonction du modèle de récupération actuel. Si vous souhaitez consulter les enregistrements de journal réels générés par les mécanismes de décrochage et de transfert et de suppression différée, remplacez simplement * par COUNT (*) dans le code fn_dblog ci-dessus et recherchez une transaction avec le nom de la transaction défini sur DeferredAllocUnitDrop::Process
.
Dans les prochains articles, je discuterai des éléments internes qui sous-tendent d'autres mythes persistants concernant les aspects de performance du moteur de stockage SQL Server.