Accueil / Ressources / Dernier backup et dernier restore, avec AlwaysOn

› Script SQL

Dernier backup et dernier restore d'une base, réplicas AlwaysOn compris.

Dans un groupe de disponibilité, les sauvegardes peuvent être faites sur le réplica principal ou sur un secondaire : l'historique de chaque réplica ne contient que ses propres sauvegardes. Pour savoir si une base est protégée, il faut regarder tous les réplicas.


Les requêtes de cette page lisent des informations de l'instance sans modifier vos données. Les commandes qui modifient quelque chose sont signalées dans le texte. Testez toujours sur un environnement non critique avant la production, et relisez chaque requête avant de l'exécuter.

Ce qu'il faut savoir avant

  • L'historique des sauvegardes est stocké dans msdb, instance par instance (tables backupset et restorehistory).
  • Sur un réplica secondaire, seules les sauvegardes complètes en copie seule (COPY_ONLY) et les sauvegardes de journal sont possibles. Les sauvegardes différentielles ne le sont pas.
  • Le groupe de disponibilité peut indiquer le réplica préféré pour les sauvegardes. La fonction sys.fn_hadr_backup_is_preferred_replica permet à un job de sauvegarde de savoir s'il doit s'exécuter.

1. Sur l'instance locale : derniers backups de chaque base

T-SQL
-- Derniers backups par base, sur l'instance où le script est exécuté
-- Droit requis : lecture de msdb. Pour un groupe de disponibilité, voir aussi la partie 3.
SET NOCOUNT ON;

SELECT
    d.name                                    AS base,
    d.state_desc                              AS etat,
    d.recovery_model_desc                     AS modele_recuperation,
    ag.name                                   AS groupe_disponibilite,
    ars.role_desc                             AS role_local,
    CASE WHEN ag.name IS NOT NULL
         THEN sys.fn_hadr_backup_is_preferred_replica(d.name) END AS replica_prefere_backup,
    (SELECT MAX(b.backup_finish_date) FROM msdb.dbo.backupset AS b
      WHERE b.database_name = d.name AND b.server_name = @@SERVERNAME
        AND b.type = 'D' AND b.is_copy_only = 0)  AS dernier_full,
    (SELECT MAX(b.backup_finish_date) FROM msdb.dbo.backupset AS b
      WHERE b.database_name = d.name AND b.server_name = @@SERVERNAME
        AND b.type = 'D' AND b.is_copy_only = 1)  AS dernier_full_copie_seule,
    (SELECT MAX(b.backup_finish_date) FROM msdb.dbo.backupset AS b
      WHERE b.database_name = d.name AND b.server_name = @@SERVERNAME
        AND b.type = 'I')                         AS dernier_differentiel,
    (SELECT MAX(b.backup_finish_date) FROM msdb.dbo.backupset AS b
      WHERE b.database_name = d.name AND b.server_name = @@SERVERNAME
        AND b.type = 'L')                         AS dernier_journal
FROM sys.databases AS d
LEFT JOIN sys.dm_hadr_database_replica_states AS drs
       ON drs.database_id = d.database_id AND drs.is_local = 1
LEFT JOIN sys.availability_groups AS ag
       ON ag.group_id = drs.group_id
LEFT JOIN sys.dm_hadr_availability_replica_states AS ars
       ON ars.replica_id = drs.replica_id AND ars.is_local = 1
WHERE d.name <> N'tempdb'
ORDER BY d.name;

2. Dernier restore de chaque base

T-SQL
-- Dernier restore enregistré pour chaque base de cette instance
SET NOCOUNT ON;

;WITH r AS (
    SELECT
        rh.destination_database_name              AS base,
        rh.restore_date                           AS date_restore,
        rh.user_name                              AS utilisateur,
        CASE rh.restore_type WHEN 'D' THEN 'Complet'
                             WHEN 'I' THEN 'Différentiel'
                             WHEN 'L' THEN 'Journal'
                             WHEN 'F' THEN 'Fichier'
                             WHEN 'G' THEN 'Groupe de fichiers'
                             WHEN 'V' THEN 'Vérification seule'
                             ELSE rh.restore_type END AS type_restore,
        bs.server_name                            AS serveur_source,
        bs.database_name                          AS base_source,
        bs.backup_finish_date                     AS sauvegarde_du,
        ROW_NUMBER() OVER (PARTITION BY rh.destination_database_name
                           ORDER BY rh.restore_date DESC) AS rang
    FROM msdb.dbo.restorehistory AS rh
    LEFT JOIN msdb.dbo.backupset AS bs
           ON bs.backup_set_id = rh.backup_set_id
)
SELECT base, date_restore, utilisateur, type_restore, serveur_source, base_source, sauvegarde_du
FROM r
WHERE rang = 1
ORDER BY base;

Un restore de vérification (type « Vérification seule ») ne prouve pas que la base est restaurable : il contrôle l'en-tête de la sauvegarde. Pour une preuve réelle, restaurez la base sur un serveur de test.

3. Tous les réplicas d'un groupe de disponibilité

Cette variante interroge chaque réplica par un serveur lié. Elle suppose qu'un serveur lié existe pour chaque réplica, avec le même nom que le réplica (colonne replica_server_name), et que l'option RPC est activée si vous voulez l'étendre.

T-SQL
-- Derniers backups d'une base sur tous les réplicas de son groupe de disponibilité
SET NOCOUNT ON;

DECLARE @Base sysname = N'MaBase';
DECLARE @sql  nvarchar(max) = N'';

-- Réplica local
SET @sql = N'SELECT @@SERVERNAME AS replica, b.database_name AS base, b.type, b.is_copy_only,
                    MAX(b.backup_finish_date) AS dernier_backup
             FROM msdb.dbo.backupset AS b
             WHERE b.database_name = @Base AND b.server_name = @@SERVERNAME
             GROUP BY b.database_name, b.type, b.is_copy_only';

-- Autres réplicas (serveurs liés portant le nom du réplica)
SELECT @sql += N' UNION ALL SELECT ' + QUOTENAME(ar.replica_server_name, '''')
             + N' AS replica, b.database_name, b.type, b.is_copy_only, MAX(b.backup_finish_date)
                FROM ' + QUOTENAME(ar.replica_server_name) + N'.msdb.dbo.backupset AS b
                WHERE b.database_name = @Base AND b.server_name = ' + QUOTENAME(ar.replica_server_name, '''')
             + N' GROUP BY b.database_name, b.type, b.is_copy_only'
FROM sys.availability_replicas AS ar
JOIN sys.availability_databases_cluster AS adc
     ON adc.group_id = ar.group_id AND adc.database_name = @Base
WHERE ar.replica_server_name <> @@SERVERNAME;

SET @sql = N'SELECT replica, base,
                    CASE type WHEN ''D'' THEN CASE WHEN is_copy_only = 1 THEN ''Full (copie seule)'' ELSE ''Full'' END
                              WHEN ''I'' THEN ''Différentiel''
                              WHEN ''L'' THEN ''Journal'' END AS type_backup,
                    dernier_backup
             FROM (' + @sql + N') AS t
             WHERE type IN (''D'', ''I'', ''L'')
             ORDER BY base, type, replica';

EXEC sys.sp_executesql @sql, N'@Base sysname', @Base = @Base;
  • Le plus récent backup, tous réplicas confondus, détermine si la base est protégée. Vérifiez que la chaîne de journaux est continue : un journal sauvegardé sur le secondaire fait partie de la même chaîne que ceux du principal.
  • Si la lecture d'un réplica échoue, vérifiez la configuration des serveurs liés et la connectivité.
  • Dans un monitoring, alertez sur le plus ancien des derniers backups de journal, pas seulement sur la date du dernier full.

Un incident, un audit, une question ?

Un premier échange suffit à cadrer l'urgence et le périmètre.

Demander une intervention