SQL Server, de la zero Lecția 31 / 40

Backup-uri: FULL, DIFF, LOG și restore drill-ul

Cele trei tipuri de backup, point-in-time recovery, cum testezi un backup pe care nu l-ai testat niciodată și orarul pe care ar trebui să-l aibă orice sistem OLTP.

Un backup pe care nu l-ai testat niciodată nu e un backup. E un fișier. Poate fi un backup bun, poate fi corupt, poate face backup la baza de date greșită — nu ai cum să știi până în ziua în care încerci să-l restaurezi. Ziua aceea e mereu o sâmbătă, mereu când telefonul tău e pe punctul de a muri și mereu când CEO-ul e în CC pe email.

Lecția asta e despre construirea unei strategii de backup în care ai cu adevărat încredere. Tipuri de backup-uri, recovery models, drill-ul de restore pe care ar trebui să-l rulezi trimestrial și nuanța specifică UE care e reglementarea de data residency.

Cele trei tipuri de backup

Backup FULL

O copie completă a bazei de date la un moment dat. Conține fiecare pagină de date și suficient din log încât backup-ul să fie consistent intern.

BACKUP DATABASE Runehold
TO DISK = N'D:\Backups\Runehold_FULL_20260315.bak'
WITH FORMAT, INIT, COMPRESSION, CHECKSUM, STATS = 10;
  • FORMAT, INIT — suprascrie fișierul dacă există. Siguranță: aproape mereu vrem un backup proaspăt, nu unul appendat.
  • COMPRESSION — fișier mult mai mic. SQL Server Enterprise modern include compresia gratis; Standard, începând cu 2022.
  • CHECKSUM — verifică paginile pe măsură ce sunt salvate; prinde corupția mai devreme.
  • STATS = 10 — afișează progresul la fiecare 10%.

Orar tipic Runehold: un backup FULL în fiecare noapte în jurul 02:00 UTC. Durează aproximativ 25 de minute.

Backup DIFFERENTIAL

Tot ce s-a schimbat de la ultimul FULL. Mai mic și mai rapid decât un FULL, dar depinde de FULL-ul disponibil.

BACKUP DATABASE Runehold
TO DISK = N'D:\Backups\Runehold_DIFF_20260315_12.bak'
WITH DIFFERENTIAL, FORMAT, INIT, COMPRESSION, CHECKSUM;

Pattern comun: FULL în fiecare noapte, DIFF la fiecare 6 ore. Restaurarea presupune FULL + cel mai recent DIFF — două fișiere în loc de multe.

Backup LOG

Toate înregistrările de log de la ultimul log backup. Esențial pentru point-in-time recovery și pentru a împiedica fișierul de log să crească nelimitat în recovery model FULL.

BACKUP LOG Runehold
TO DISK = N'D:\Backups\Runehold_LOG_20260315_1245.trn'
WITH FORMAT, INIT, COMPRESSION, CHECKSUM;

Orar tipic: la fiecare 15 minute în timpul programului de lucru. Asta înseamnă că dacă baza de date pică, pierzi cel mult 15 minute de modificări (RPO — Recovery Point Objective = 15 min).

Recovery models din nou

Din lecția 30:

  • SIMPLE — fără log backups posibile. RPO = ultimul FULL sau DIFF.
  • FULL — log backups obligatorii; point-in-time recovery; logul rămâne mic între backups.
  • BULK_LOGGED — de nișă.

Regulă: orice bază de date de producție care conține date pe care ai plânge să le pierzi ar trebui să fie pe FULL recovery cu log backups regulate. Orice altceva — DW de raportare, dev, test, scratch de data lake — poate fi pe SIMPLE.

Restaurarea: drill-ul efectiv

Nu poți spune că ai backup-uri dacă n-ai făcut un restore. Iată drill-ul pe care fiecare echipă ar trebui să-l ruleze cel puțin trimestrial.

Pasul 1: restaurează ultimul FULL

RESTORE DATABASE Runehold_Recovery
FROM DISK = N'D:\Backups\Runehold_FULL_20260315.bak'
WITH
    MOVE N'Runehold'     TO N'E:\RestoreTest\Runehold.mdf',
    MOVE N'Runehold_log' TO N'E:\RestoreTest\Runehold_log.ldf',
    NORECOVERY,
    REPLACE,
    STATS = 5;
  • MOVE — pentru că restaurezi într-o locație diferită (sau pe altă mașină) cu căi diferite. Fără MOVE, SQL Server încearcă să pună fișierele unde erau inițial, ceea ce eșuează dacă acea cale nu există.
  • NORECOVERY — lasă baza de date în starea „restoring”; gata să aplici alte backup-uri.
  • REPLACE — suprascrie dacă există o bază de date cu același nume (rar, dar util).

Pasul 2: aplică ultimul DIFF

RESTORE DATABASE Runehold_Recovery
FROM DISK = N'D:\Backups\Runehold_DIFF_20260315_12.bak'
WITH NORECOVERY;

Pasul 3: aplică log backups până la momentul țintă

RESTORE LOG Runehold_Recovery
FROM DISK = N'D:\Backups\Runehold_LOG_20260315_1215.trn'
WITH NORECOVERY;

RESTORE LOG Runehold_Recovery
FROM DISK = N'D:\Backups\Runehold_LOG_20260315_1230.trn'
WITH NORECOVERY;

-- Oprește la un moment specific
RESTORE LOG Runehold_Recovery
FROM DISK = N'D:\Backups\Runehold_LOG_20260315_1245.trn'
WITH STOPAT = N'2026-03-15 12:38:42', NORECOVERY;

Pasul 4: adu baza de date online

RESTORE DATABASE Runehold_Recovery WITH RECOVERY;

Pasul 5: verifică

Conectează-te. Interoghează. Verifică numărul de rânduri față de ce te așteptai. Alege câteva tabele și verifică că rândurile arată cum trebuie.

Pasul 6: documentează ce ai făcut și cât a durat

RTO — Recovery Time Objective — e downtime-ul maxim acceptabil. Dacă restore drill-ul tău a durat 45 de minute, RTO-ul tău e de 45 de minute. Dacă leadership-ul așteaptă 15 minute, ai un decalaj și trebuie să lucrezi la asta.

Rulează drill-ul ăsta în fiecare trimestru. Rotește cine-l conduce, ca să știe pașii mai mulți oameni.

Găsirea și aplicarea lanțului corect de log

Când lovește dezastrul, trebuie să cunoști fiecare backup din lanț. Script rapid:

-- Istoric FULL + DIFF + LOG pentru o bază de date
WITH history AS (
    SELECT bs.database_name,
           bs.type,
           bs.backup_start_date,
           bs.backup_finish_date,
           bmf.physical_device_name
    FROM msdb.dbo.backupset AS bs
    JOIN msdb.dbo.backupmediafamily AS bmf ON bmf.media_set_id = bs.media_set_id
    WHERE bs.database_name = 'Runehold'
      AND bs.backup_start_date >= DATEADD(DAY, -7, GETDATE())
)
SELECT * FROM history ORDER BY backup_start_date;

Arată fiecare backup pe care SQL Server Agent l-a înregistrat. La îndemână în timpul unui dezastru.

Verificarea backup-urilor înainte de dezastru

RESTORE VERIFYONLY verifică un fișier de backup pentru corupție de bază fără să-l restaureze efectiv:

RESTORE VERIFYONLY FROM DISK = N'D:\Backups\Runehold_FULL_20260315.bak';

Rapid, ieftin, sigur. Fiecare job de backup ar trebui să ruleze RESTORE VERIFYONLY imediat după ce backup-ul e gata. Prinde backup-uri corupte înainte să ai nevoie de ele.

Și: periodic fă un test complet de restore (drill-ul de mai sus) ca să prinzi probleme pe care VERIFYONLY le ratează. La câteva luni.

Backup în Azure Blob sau S3

SQL Server modern poate face backup direct în Azure Blob Storage (și cu unelte specifice, în S3). Copii off-site fără niciun script de copiere de fișiere.

BACKUP DATABASE Runehold
TO URL = N'https://runehold.blob.core.windows.net/backups/Runehold_20260315.bak'
WITH CREDENTIAL = N'AzureStorageCredential',
     COMPRESSION, CHECKSUM, FORMAT, INIT;

CREDENTIAL e un obiect SQL Server care ține token-ul SAS Azure. Configurează o singură dată, folosește pentru totdeauna.

Pentru companii UE precum Runehold, backup-urile off-site stau de obicei într-o regiune Azure exclusiv UE sau într-un bucket S3 exclusiv UE. Conformitatea cu data-residency contează.

GDPR, backup-uri și „dreptul de a fi uitat”

O treabă jenantă. Când un client își exercită dreptul GDPR la ștergere, trebuie să-i ștergi datele. Ușor în tabelele live. Nu e ușor în backup-uri — un backup luat ieri conține rândul pe care trebuie să-l ștergi azi.

Abordare standard de conformitate: documentezi în politica de confidențialitate că backup-urile sunt păstrate pentru o perioadă definită (adesea 30-90 de zile), iar ștergerea se aplică imediat la datele live, plus în backup-uri pe măsură ce backup-urile vechi ies din fereastra de retenție. Nu editezi backup-uri vechi; le rotești.

Analiza juridică completă e în afara scopului aici. Discută cu cine deține funcția de DPO (Data Protection Officer) la voi. Doar fii conștient că intersecția dintre backup-uri și GDPR are răspunsuri documentate; nu le inventa pe ale tale.

Rulează asta pe propria mașină

USE master;
GO

-- 1. Ia un backup full pe disc
BACKUP DATABASE Runehold
TO DISK = N'C:\Temp\Runehold_FULL.bak'
WITH FORMAT, INIT, COMPRESSION, CHECKSUM, STATS = 10;

-- 2. Verifică-l
RESTORE VERIFYONLY FROM DISK = N'C:\Temp\Runehold_FULL.bak';

-- 3. Backup la log (necesită FULL recovery)
ALTER DATABASE Runehold SET RECOVERY FULL;
BACKUP DATABASE Runehold TO DISK = N'C:\Temp\Runehold_FULL.bak' WITH FORMAT, INIT;
BACKUP LOG Runehold TO DISK = N'C:\Temp\Runehold_LOG.trn' WITH FORMAT, INIT;

-- 4. Restaurează cu un nume nou (drill-ul)
RESTORE DATABASE Runehold_Recovery
FROM DISK = N'C:\Temp\Runehold_FULL.bak'
WITH
    MOVE N'Runehold'     TO N'C:\Temp\Runehold_R.mdf',
    MOVE N'Runehold_log' TO N'C:\Temp\Runehold_R.ldf',
    NORECOVERY, REPLACE;

RESTORE LOG Runehold_Recovery
FROM DISK = N'C:\Temp\Runehold_LOG.trn'
WITH RECOVERY;

-- 5. Verifică baza de date restaurată
SELECT COUNT(*) FROM Runehold_Recovery.Sales.Customer;
SELECT COUNT(*) FROM Runehold.Sales.Customer;
-- Ar trebui să se potrivească.

-- 6. Aruncă restore-ul de test
DROP DATABASE Runehold_Recovery;

Rulează-l de la cap la coadă o dată. Fă-l să devină memorie musculară. Ziua în care va trebui să faci asta pe prod nu va fi o zi bună, dar va fi una rapidă.

Urmează: SQL Server Agent — cum programezi backup-urile de mai sus, plus toată cealaltă rutină repetabilă pe care un DBA o automatizează.

Caută