Situatie
In practica apar de multe ori alerte de spatiu care pot duce la efecte catastrofale pentru productie, de aceea trebuie ca prim pas sa intelegem cum se reduc acestea.
Solutie
Pasi de urmat
Se utilizeaza SSMS instrumetul de baza sau Azure Data Studio (cross-platform).
SSMS = SQL Server Management Studio
Prin SSMS poți să:
- navighezi prin baze de date, tabele, coloane, indexuri ca într-un File Explorer
- scrii și execuți interogări T-SQL într-un editor cu autocomplete și evidențiere sintaxă
- gestionezi utilizatori, permisiuni și roluri
- faci backup și restore cu câteva clicuri
- monitorizezi activitatea serverului (Activity Monitor)
- configurezi SQL Server Agent pentru joburi programate
- faci exact operațiile din materialul de mai sus — shrink, compresie, autogrowth — fie grafic (clic dreapta → Tasks), fie prin scripturi.
Pasul 1 – SPATIUL PE FIECARE BAZA DE DATE
SELECT name AS [Baza de date], ROUND(SUM(size) * 8.0 / 1024, 1) AS [Dimensiune totala MB], ROUND(SUM(FILEPROPERTY(name, 'SpaceUsed')) * 8.0 / 1024, 1) AS [Spatiu utilizat MB], ROUND((SUM(size) - SUM(FILEPROPERTY(name, 'SpaceUsed'))) * 8.0 / 1024, 1) AS [Spatiu liber MB] FROM sys.master_files GROUP BY name ORDER BY [Dimensiune totala MB] DESC;
Pasul 2 – Spatiu per fisier (MDF/LDF)
USE [NumeleBaseDeDate]; GO SELECT name AS [Fisier], type_desc AS [Tip], ROUND(size * 8.0 / 1024, 1) AS [Dimensiune MB], ROUND(FILEPROPERTY(name, 'SpaceUsed') * 8.0 / 1024, 1) AS [Utilizat MB], ROUND((size - FILEPROPERTY(name, 'SpaceUsed')) * 8.0 / 1024, 1) AS [Liber MB] FROM sys.database_files;
Pasul 3 – Cele mai mari tabele din baza de date
SELECT TOP 20 t.name AS [Tabel], ROUND(SUM(a.total_pages) * 8.0 / 1024, 1) AS [Total MB], ROUND(SUM(a.used_pages) * 8.0 / 1024, 1) AS [Utilizat MB], ROUND(SUM(a.data_pages) * 8.0 / 1024, 1) AS [Date MB] FROM sys.tables t INNER JOIN sys.indexes i ON t.object_id = i.object_id INNER JOIN sys.partitions p ON i.object_id = p.object_id AND i.index_id = p.index_id INNER JOIN sys.allocation_units a ON p.partition_id = a.container_id WHERE t.is_ms_shipped = 0 GROUP BY t.name ORDER BY [Total MB] DESC;
Dacă spațiul liber din MDF depășește 30%, ai candidat pentru shrink sau arhivare. Dacă LDF-ul crește continuu, verifică modelul de recuperare și planul de backup.
SHRINK este un workaround de urgență, nu o operație de mentenanță regulată. Cauzează fragmentare severă a index-urilor și degradare de performanță. Folosește-l doar când ai nevoie urgentă de spațiu pe disc.
Dezactivează AUTO_SHRINK (obligatoriu)
-- Verificare stare curenta SELECT name, is_auto_shrink_on FROM sys.databases WHERE is_auto_shrink_on = 1; -- Dezactivare (rulat per baza de date) ALTER DATABASE [NumeleBaseDeDate] SET AUTO_SHRINK OFF; GO
Shrink fișier date (MDF) — modul recomandat
USE [NumeleBaseDeDate]; GO -- Shrink cu target de 10% spatiu liber ramas DBCC SHRINKFILE (N'NumeFisierDate', 10); -- Varianta: shrink la dimensiune specifica (ex. 500 MB) DBCC SHRINKFILE (N'NumeFisierDate', 500); GO Shrink baza de date completă (SSMS — clic dreapta)
Echivalentul T-SQL al optiunii din SSMS -- Tasks → Shrink → Database DBCC SHRINKDATABASE (N'NumeleBaseDeDate', 10); GO
Obligatoriu după shrink: reconstruiește index-urile pentru a elimina fragmentarea creată.
Rebuild indexuri după shrink
USE [NumeleBaseDeDate]; GO -- Rebuild toate indexurile din toate tabelele EXEC sp_MSforeachtable @command1 = 'ALTER INDEX ALL ON ? REBUILD WITH (ONLINE = ON)'; GO -- Alternativa fara sp_MSforeachtable DECLARE @sql NVARCHAR(MAX) = N'';
SELECT @sql += 'ALTER INDEX ALL ON [' + s.name + '].[' + t.name + '] REBUILD;' + CHAR(13) FROM sys.tables t JOIN sys.schemas s ON t.schema_id = s.schema_id; EXEC sp_executesql @sql; GO
Workflow complet shrink în SSMS (interfață grafică)
1.Click dreapta pe baza de date → Tasks → Shrink → Files
2.Selectează File type: Data (sau Log pentru LDF)
3.Alege “Release unused space” sau setează dimensiunea dorită
4.Click OK și monitorizează progresul în Activity Monitor
5.După finalizare, rulează rebuild indexuri (scriptul de mai sus).
Fișierul LDF (transaction log) crește adesea necontrolat și este una din principalele cauze de epuizare a spațiului pe serverele cu model de recuperare FULL.
–– Vizualizare utilizare log per baza de date
DBCC SQLPERF(LOGSPACE); -- Detalii VLF si motiv blocare truncare SELECT log_reuse_wait_desc AS [Motiv blocare], name AS [Baza de date] FROM sys.databases WHERE log_reuse_wait_desc <> 'NOTHING'; GO
Motivele comune pentru care LDF-ul nu se poate micsora:
- LOG_BACKUP Nu s-a efectuat niciun backup de log. Soluție: fă un backup log, apoi shrink.
- ACTIVE_TRANSACTION Există tranzacții deschise / rulând de mult timp. Soluție: identifică și termină tranzacția.
- REPLICATION Replicare sau CDC activă blochează truncarea. Verifică că agenții funcționează.
- DATABASE_MIRRORING Mirroring activ — partenerul trebuie să fie sincronizat.
Shrink LDF — model SIMPLE
-- In modelul SIMPLE, truncarea e automata dupa checkpoint -- Shrink direct: USE [NumeleBaseDeDate]; GO DBCC SHRINKFILE (N'NumeFisierLog', 1); GO Shrink LDF — model FULL (pași obligatorii)
-- Pas 1: Backup log pentru a marca VLF-urile ca inactive BACKUP LOG [NumeleBaseDeDate] TO DISK = 'D:\Backup\NumeleBaseDeDate_log.bak' WITH NOFORMAT, NOINIT, STATS = 10; GO -- Pas 2: Shrink fisierul log USE [NumeleBaseDeDate]; GO
DBCC SHRINKFILE (N'NumeFisierLog', 1); GO -- Verifica rezultatul SELECT name, ROUND(size * 8.0 / 1024, 1) AS [MB dupa shrink] FROM sys.database_files WHERE type = 1; Best practice: în model FULL, programează backup-uri de log la fiecare 15–30 minute cu SQL Agent. Fără backup regulat de log, LDF-ul crește la infinit și nu poate fi recuperat fără backup.
Soluțiile pe termen lung elimină nevoia de shrink repetat. Abordează cauza reală a creșterii spațiului.
1 — Compresie date (row / page)
-- Estimeaza economiile INAINTE de aplicare EXEC sys.sp_estimate_data_compression_savings @schema_name = 'dbo', @object_name = 'NumeleTabelului', @index_id = NULL, @partition_number = NULL, @data_compression = 'PAGE'; -- Aplica compresie PAGE pe un tabel ALTER TABLE [dbo].[NumeleTabelului] REBUILD WITH (DATA_COMPRESSION = PAGE); -- Aplica compresie pe un index specific ALTER INDEX [IX_NumeIndex] ON [dbo].[NumeleTabelului] REBUILD WITH (DATA_COMPRESSION = PAGE); GO Compresia PAGE poate reduce spațiul cu 30–50% pe tabele cu date repetitive. Necesită CPU suplimentar la citire/scriere, ideal pentru date istorice sau de arhivă.
2 — Arhivare date vechi
-- Exemplu: muta date mai vechi de 2 ani intr-un tabel de arhiva BEGIN TRANSACTION; INSERT INTO [dbo].[TabelArhiva] (col1, col2, col3, data_inregistrare) SELECT col1, col2, col3, data_inregistrare FROM [dbo].[TabelPrincipal] WHERE data_inregistrare < DATEADD(YEAR, -2, GETDATE()); DELETE FROM [dbo].[TabelPrincipal] WHERE data_inregistrare < DATEADD(YEAR, -2, GETDATE()); COMMIT TRANSACTION; GO
Rulează DELETE în batches (ex. câte 10.000 rânduri) pentru a evita creșterea explozivă a LDF-ului și blocarea tabelelor.
3 — Ștergere date în batches (fără explozie log)
-- DELETE in batches de 10.000 randuri DECLARE @BatchSize INT = 10000; DECLARE @RowsDeleted INT = 1; WHILE @RowsDeleted > 0 BEGIN DELETE TOP (@BatchSize) FROM [dbo].[TabelPrincipal] WHERE data_inregistrare < DATEADD(YEAR, -2, GETDATE()); SET @RowsDeleted = @@ROWCOUNT; WAITFOR DELAY '00:00:01'; -- pauza 1 secunda intre batches END GO 4 — Partiționare tabele mari pe date
-- Creeaza o functie de partitionare pe an
CREATE PARTITION FUNCTION PF_AnInregistrare (DATE)
AS RANGE RIGHT FOR VALUES
('2022-01-01', '2023-01-01', '2024-01-01', '2025-01-01');
-- Creeaza schema de partitionare
CREATE PARTITION SCHEME PS_AnInregistrare
AS PARTITION PF_AnInregistrare
ALL TO ([PRIMARY]);
-- Aplica pe tabelul nou (sau recreeaza tabelul)
CREATE TABLE [dbo].[TabelPartitionat] (
id INT NOT NULL,
data_inregistrare DATE NOT NULL,
valoare NVARCHAR(200)
) ON PS_AnInregistrare(data_inregistrare);
GO
Partiționarea permite partition switching — arhivare instantanee a unui întreg segment fără DELETE, fără impact pe log.
Configurarea greșită a autogrowth este cauza numărul 1 a fișierelor log cu mii de VLF-uri și a creșterilor imprevizibile de spațiu.
Verifică configurarea autogrowth curentă
SELECT
DB_NAME(database_id) AS [Baza de date],
name AS [Fisier],
type_desc AS [Tip],
ROUND(size * 8.0 / 1024, 1) AS [Dimensiune MB],
is_percent_growth AS [Crestere in %],
CASE is_percent_growth
WHEN 1 THEN CAST(growth AS VARCHAR) + '%'
ELSE CAST(ROUND(growth * 8.0 / 1024, 0) AS VARCHAR) + ' MB'
END AS [Increment crestere],
ROUND(max_size * 8.0 / 1024, 0) AS [Dimensiune maxima MB]
FROM sys.master_files
ORDER BY DB_NAME(database_id), type;
Setează autogrowth corect (valori fixe, nu procente)
-- GREȘIT: autogrowth procentual (creste haotic pe fisiere mari) -- ALTER DATABASE [NumeleBaseDeDate] MODIFY FILE (NAME='NumeFisier', FILEGROWTH=10%) -- CORECT: autogrowth in MB, valori fixe -- Pentru fisierul de date (MDF): increment 512 MB sau 1 GB ALTER DATABASE [NumeleBaseDeDate] MODIFY FILE (NAME = N'NumeFisierDate', FILEGROWTH = 512MB); -- Pentru fisierul log (LDF): increment 256 MB (sub 1 GB) ALTER DATABASE [NumeleBaseDeDate] MODIFY FILE (NAME = N'NumeFisierLog', FILEGROWTH = 256MB); GO Verifică numărul de VLF-uri (SQL Server 2016+)
-- VLF-uri per baza de date (SQL Server 2016+) SELECT [name] AS [Baza de date], COUNT(*) AS [Nr VLF-uri] FROM sys.databases CROSS APPLY sys.dm_db_log_info(database_id) GROUP BY [name] ORDER BY [Nr VLF-uri] DESC; -- Sub 50 VLF = optim -- Peste 500 VLF = problema de performanta la backup/recovery Resetare VLF — reducere număr excesiv
-- ATENTIE: rulat DOAR in fereastra de mentenanta! USE [NumeleBaseDeDate]; GO -- Pas 1: backup log BACKUP LOG [NumeleBaseDeDate] TO DISK = 'NUL'; -- Pas 2: shrink fisier log la minim DBCC SHRINKFILE (N'NumeFisierLog', 1); -- Pas 3: redimensioneaza la dimensiunea initiala dorita ALTER DATABASE [NumeleBaseDeDate] MODIFY FILE (NAME = N'NumeFisierLog', SIZE = 512MB, FILEGROWTH = 256MB); GO
Leave A Comment?