Inside SQL Server, Microsoft Press.
Traduction personnelle. But pédagogique.


Journalisation des Transactions et Récupération

Le journal des transactions enregistre tous les changements apportés à la base et stocke suffisamment d'informations pour permettre que les changements soit annulés (rolled back) ou rejoués (rolled forward) dans le cas ou un crash système ou demandés explicitement par une application (dans le cas d'une commande rollback). Physiquement, le journal des transactions est un ensemble de fichier associé à la base. Les modules qui réalisent les actions de modification sur la base écrivent des entrées au journal qui décrivent exactement les changements à réaliser. Chaque entrée au journal est associé à un LSN (Log Sequence Number) unique. Toutes les entrées faisant parties d'une même transaction sont liées pour faciliter les actions d'annulation (comme avec un rollback) et les actions de réapplication (comme une récupération système).

Le gestionnaire de buffer garanti que le journal est écrit avant que les changements à la base soient écrits. SQL Server garde trace de sa position courante dans le journal en terme de LSN. Chaque fois qu'une page est modifiée, le LSN correspondant à l'entrée du journal est inscrit dans l'entête de la page de données. Les pages dirty peuvent être écrites seulement si le LSN de chaque page est inférieur au LSN de la dernière page écrite dans le journal. Le gestionnaire de buffer garanti aussi que les pages du journal sont écrites dans un ordre spécifique, permettant ainsi de savoir quelles sont les pages de journal à traiter après un crash système, indépendamment de quand le problème est survenu. Les enregistrements du journal pour une transaction sont écrits sur disque avant que la validation soit envoyée au client, mais les données modifiées peuvent ne pas avoir été physiquement appliquées sur les pages de données. Bien que les écritures au journal doivent être synchrones, les écritures aux pages de données peuvent être asynchrones. 

La journalisation inclut le démarrage et la fin de chaque transaction (et les savepoints, si une transaction les utilise). Entre les signes de début et de fin de transaction, des informations existent à propos des changements opérés sur les données. Ces informations se présentent sous la forme de valeurs de données "avant et après", ou se référent à l'opération dont elles sont issues. La fin typique d'une transaction est marquée par une entrée Commit, qui indique que la transaction doit être appliquée dans la base ou rejouée si nécessaire. Une annulation de transaction en tant normal (hors d'un redémarrage de système) due à une annulation explicite ou une erreur de ressource (par exemple, manque de mémoire), annule l'opération en appliquant les changements qui annulent les modifications aux données originales. Ces changements sont marqués au journal en tant qu'enregistrements de compensation (compensation log records). Si le système tombe après la validation de transaction mais avant que les données soient appliquées aux pages de données, la transaction doit être récupérable. Le processus de récupération est lancé automatiquement au démarrage du système. Ce processus est lancé aussi à l'étape finale du processus de restauration d'une base à partir d'une sauvegarde et peut être forcé manuellement.

La récupération rejoue (rollforward) et annule (rollback) les opérations. Dans la phase où les opérations sont rejouées, le journal est examiné et chaque changement est vérifié pour être déjà réfléchi dans la base. (Après, chaque changement fait par la transaction est garanti d'être appliqué.) Si la modification n'apparaît pas dans la base, il est rejoué à partir de l'information présente dans le journal. Une opération d'annulation requiert la suppression des changements partiels de la transaction lorsqu'elle n'a pas été complètement validée.

Pendant la récupération, seul les changements produits ou en cours de traitements depuis le dernier checkpoint sont rejoués ou annulés. Il y a trois phases dans l'algorithme de récupération, et sont centrés autour du dernier enregistrement de checkpoint dans le journal des transactions. Ces trois phases sont illustrées dans la Figure 3-5. Ces descriptions sont références à un crash de  SQL Server, mais les mêmes plans de récupération sont mis en place si SQL Server est stoppé intentionnellement.

Figure 3-5. Les trois phases du processus de récupération de SQL Server.

Récupération et Verrouillage

Le verrouillage, la gestion des transactions, et la récupération sont des sujets très liés. Une transaction ne peut être annulée que si les données affectées sont verrouillées exclusivement de telle manière qu'aucun autre processus ne puisse voir les changements en cours (qui peuvent être encore annulés) ou modifier des ressources utilisées par la transaction et qui pourraient l'empêcher d'être annulée. Seulement une transaction active peut modifier un enregistrement à un moment donné. C'est pourquoi un verrou exclusif doit être maintenu tant que la transaction n'est pas validée ou annulée. Une transaction s'exécutant avec le niveau d'isolation Read Uncommitted (dirty read) peu parfois lire des données qui n'existent pas logiquement car elle n'a pas de verrous exclusifs. Mais n'importe quelle autre transaction utilisant un niveau d'isolation supérieur (celui par défaut— le mode Read Uncommitted) ne permet pas ce genre de phénomène.

LSNs de Page et Récupération

Chaque page de base de données a une LSN dans l'entête de page qui l'identifie de manière unique, par version, au fur et à mesure que les enregistrements de la page sont modifiés. Ce LSN de page identifie la dernière entrée dans le journal des transactions qui a modifié l'enregistrement sur cette page. Pendant la réapplication de transactions, le LSN de chaque entrée du journal est comparé au LSN de la page de données que l'entrée du journal a modifiée; si le LSN de la page est inférieur au LSN du journal, l'opération indiquée dans l'entrée du journal est annulée, montrée comme dans la Figure 3-6.

Du fait que la récupération trouve le dernier checkpoint dans le journal (plus les transactions encore en activité au moment du checkpoint) et démarre à partir de ce point, le temps de récupération est court et le journal des événements peut être purgé ou archivé pour toutes les modifications validées avant le checkpoint. Sinon, la récupération pourrait durer plus longtemps et le journal des transactions deviendrait volumineux. Un journal de transaction ne peut pas être purgé au-delà du point  de la plus proche transaction ouverte, quel que soit le nombre de checkpoints survenu. Si une transaction reste ouverte, le journal doit être préservé car SQL Server ne sait pas si la transaction est terminée ou pas. La transaction peut être candidate à une validation ou une annulation.

Certains administrateurs SQL Server ont noté que le journal des transactions n'est pas capable de libérer de l'espace, même après la purge de celui-ci. Ce problème résulte souvent de quelques processus ayant ouvert une transaction et qui ont été oubliés. Pour cette raison, du point de vue du développement d'applications, il faut veiller à ce que les transactions soient courtes. Une autre raison pour ce problème tient dans le fait qu'une table étant répliquée utilise la réplication transactionnelle alors que le log reader ne l'a pas encore traité. Vous pouvez utiliser DBCC OPENTRAN pour voir la dernière transaction ouverte, ou la plus veille transaction répliquée non encore traitée, et alors appliquer les mesures correctives (comme la terminaison du processus ou lancer la procédure stockée sp_repldone pour permettre aux transactions répliquées d'être purgées).

Figure 3-6. Comparaison des LSNs pour décider le traiter ou non l'entrée du journal.