I. Préface

La plupart des SGBDR dignes de ce nom se doivent de maintenir leurs données consistantes en quelque moment que ce soit. Il faut donc un processus permettant

  • la récupération des données et le retour en un état transactionnel consistant en cas de crash au milieu d'activité ;
  • le retour en arrière d'une transaction au cas où une erreur apparaîtrait et la récupération des anciennes valeurs ;
  • la sauvegarde consistante de la base de données pendant la période d'activité ;
  • la réplication des modifications effectuées dans des environnements distribués.

C'est donc le journal de transactions qui sera le garant de toutes ces différentes fonctionnalités.

Sous Sybase ASE, le journal de transactions est en fait stocké dans une table propre à chaque base de données : la table système syslogs.

Elle n'est pas utilisable en clair et est remplie séquentiellement.

C'est cette table, et plus particulièrement la gestion de la place qu'elle utilise sur disque qui pose un certain nombre de problèmes aux DBA en herbe.

II. Principe de fonctionnement

Prenons une transaction bancaire consistant à débiter et créditer deux comptes.

La transaction doit être atomique : soit les deux mouvements passent, soit aucun ne doit passer, soit…

  • supprimer CHF 1000.-- du compte A ;
  • ajouter CHF 1000.-- au nouveau compte B ;
  • si tout s'est bien passé, valider, sinon invalider.

Ce qui s'écrit, en T-SQL

 
Sélectionnez
BEGIN TRAN T2

UPDATE CompteA SET Valeur=Valeur-1000 WHERE Noclient=999

if @@error <> 0 

   ROLLBACK

insert into CompteB (NoClient, Valeur) values (999,1000) 

if @@error <> 0 

   ROLLBACK

COMMIT

Que se passe-t-il au niveau du journal de transactions ?

III. Impact sur les sauvegardes

Il faut ici déterminer quel type de sauvegarde vous effectuez.

III-A. Sauvegarde de base de données complète uniquement

Dans le cas où vous n'effectuez que des sauvegardes de base de données, vous devez tenir compte des points suivants

  • vous acceptez implicitement la perte de données qui pourrait se produire depuis votre dernière sauvegarde ;
  • vous vous interdisez la possibilité de revenir en arrière de quelques transactions en cas de DELETE/UPDATE malvenus.

Cela étant posé, cette façon de faire offre les intérêts suivants :

  • simplification de la gestion des journaux de transactions ;
  • simplification de la gestion des sauvegardes.

Dans ce cas, il vous suffit de mettre l'option "trunc log on checkpoint" à votre base de données. Ce faisant, la partie inactive du journal de transactions sera nettoyée à chaque fois qu'un checkpoint (à savoir une écriture des blocs de la mémoire aux disques) s'effectuera.

 
Sélectionnez
USE master

GO

sp_dboption Votrebase, 'trunc log', true

GO

Dans le même ordre d'idée, vous pouvez déterminer ce qui se passera si une transaction venait à remplir le journal. Vous pouvez choisir de tout arrêter tant que de la place ne se fait pas... dans ce cas, toute modification de cette base sera gelée. C'est l'option par défaut.

Sinon vous pouvez opter de tuer la transaction saturant le log automatiquement :

USE master

GO

sp_dboption Votrebase, 'abort tran on log full', true

GO

III-B. Sauvegarde de la base de données et des journaux de transactions

IV. Vidange manuelle

Pour vidanger manuellement un journal de transactions plein, on doit exécuter la commande suivante :

 
Sélectionnez
DUMP TRAN MaBase WITH TRUNCATE_ONLY

Cette commande va tronquer toutes les transactions fermées apparaissant dans le journal, jusqu'à la première transaction ouverte.

Image non disponible

L'ASE doit cependant écrire dans ledit journal ce qu'il est en train de faire… ce qui pose un sérieux problème si le journal est totalement saturé. Dans ce cas, il y a possibilité de lui demander de tronquer le log sans spécifier quoi que ce soit dans le journal via

 
Sélectionnez
DUMP TRAN MaBase WITH NOLOG
Image non disponible

C'est une méthode assez brutale, à n'utiliser qu'en ultime recours, car si un problème matériel venait à apparaître à ce moment, la récupération automatique de la base pourrait être problématique : l'ASE essayerait de remonter un journal de transactions qu'il ne serait pas à même de trouver ! En effet, l'ASE cherchera à remonter une transaction dont l'ID se trouve dans master..syslogshold… et cette transaction ne sera plus présente dans Votrebase..syslogs !

Il y a encore un cas de figure problématique : c'est celui de la transaction qu'un développeur aurait oublié de fermer.

Dans ce cas, le journal de transactions se retrouve saturé par une seule transaction ouverte, donc impossible à vidanger.

La solution consiste alors à tuer toutes les transactions ouvertes de la base problématique : ce faisant, la session problématique sera tuée et sa transaction rollbackée… et donc supprimable.

La requête suivante vous génère les ordres SQL à exécuter pour tuer vos sessions. Il faut donc rejouer l'output de cette requête.

 
Sélectionnez
SELECT 'KILL '+convert(char(5), spid) 

from master..sysprocesses 

where dbid=db_id('MaBase')

… puis réessayer le DUMP TRAN.

Un autre cas de bord viendrait du fait que votre base aurait été une fois une base répliquée, utilisée par Sybase Replication Server ou SQL Remote. Dans ce cas, un deuxième point de troncature, nommé LTM (Log Transaction Mark) est présent et suit, avec une certaine latence, le point de troncature… et dans ce cas précis, le dump tran ne peut sectionner une transaction n'ayant pas passé dans la moulinette de la réplication.

Image non disponible

Il est assez aisé de se rendre compte qu'un LTM existe en testant la commande suivante : un LTM retourne une adresse supérieure à 0.

 
Sélectionnez
use VotreBase

GO

dbcc gettrunc

GO 

Dans ce cas, il y a un moyen de supprimer ce point de troncature, tout en étant conscient de l'impact que cela pourrait avoir dans le cas où votre base serait réellement une base source de réplication !

 
Sélectionnez
USE VotreBase

GO

dbcc settrunc('ltm', 'ignore')

GO

... et dans le cas où ce problème se passerait en phase de récupération (et donc que la base ne pourrait être ouverte)

 
Sélectionnez
use VotreBase

GO

dbcc dbrepair (VotreBase, ltmignore)

GO

V. Vidange automatique

Il y a possibilité de créer une procédure stockée sp_thresholdaction.

Soit il vous est possible de la créer dans la base sybsystemprocs, et dans ce cas elle sera active pour toutes vos bases.

Soit il vous est possible de la créer dans une base spécifique, et dans ce cas elle sera active dans cette base uniquement, en suppléant au besoin une sp_thresholdaction créée dans sybsystemprocs.

Voici un exemple de procédure de seuil. Notez que vos pouvez modifier à votre guise le corps de la procédure, mais ni son nom, ni ses paramètres d'entrée :

 
Sélectionnez
create procedure sp_thresholdaction 

        @dbname varchar(30),

        @segmentname varchar(30),

        @space_left int,

        @status int

as

        dump transaction @dbname with truncate_only

Il va sans dire que cet exemple est le plus basique qu'il soit. Nous pouvons l'enrichir à notre guise :

 
Sélectionnez
CREATE PROC sp_thresholdaction (

  @dbname varchar(30),

  @segmentname varchar(30),

  @space_left int,

  @status int )  

AS

BEGIN

declare @msg  varchar(80),

        @date_actuelle  datetime,

        @nom_fichier  varchar(100),

        @date_fichier  varchar(20),

        @rep_sauve_log  varchar(40)



select @rep_sauve_log = C:\backup\log\'



select @date_actuelle = getdate()



select @date_fichier =

   convert(varchar(4),datepart(YYYY,@date_actuelle)) +

   convert(varchar(2),datepart(MM,@date_actuelle)) +

   convert(varchar(2),datepart(DD,@date_actuelle)) +'_'+

   convert(varchar(2),datepart(HH,@date_actuelle)) +

   convert(varchar(2),datepart(MI,@date_actuelle))



select @nom_fichier = @rep_sauve_log + @dbname + @date_fichier + '.trn',

       @msg = 'ATTENTION: Seuil de la dernière chance atteint sur la base ' + @dbname + ', segment ' + @segmentname 



print @msg



if @segmentname = 'logsegment'

     dump tran @dbname to @nom_fichier



return

END

Relevons que cette procédure est donc lancée automatiquement par l'ASE.

Il est par contre possible d'ajouter des seuils à n'importe quel niveau et sur n'importe quel segment en l'activant via sp_addthreshold.

Voici donc un exemple pour ajouter un seuil sur un segment d'index de la base MonTest.

 
Sélectionnez
sp_addthreshold @dbname='MonTest', @segname='index_seg', @freespace=10, @proc_name='sp_thresholdaction'