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

Un health check SQL Server de 30 de minute: scriptul pe care chiar îl rulezi

Checklist-ul ordonat pe care îl rulez pe orice SQL Server pe care nu l-am mai văzut. Imprimabil, lipibil și lecția de încheiere a cursului.

Ți se dau cheile unui SQL Server pe care nu l-ai mai văzut niciodată. „Spune-ne ce nu e în regulă cu el până la ora 17.”

Lecția de față e checklist-ul de 30 de minute pe care îl rulez. Fiecare interogare, în ordine, cu lucrul pe care îl cauți și ce să faci dacă-l găsești. E lecția de încheiere a cursului pentru că reunește tot ce am acoperit. Poți să o printezi. Poți să o lipești în trusa ta. În ziua în care un client nou îți dă un SQL Server, scoți asta și ești productiv într-o jumătate de oră.

Mulțumesc că ai rămas alături prin 40 de lecții. Să încheiem cu o singură rulare scriptată prin întreaga trusă DBA.

Pasul 1 — Prezentarea generală a serverului (2 minute)

SELECT
    @@SERVERNAME,
    @@VERSION,
    SERVERPROPERTY('Edition'),
    SERVERPROPERTY('ProductVersion'),
    SERVERPROPERTY('MachineName'),
    cpu_count, hyperthread_ratio, physical_memory_kb / 1024 AS physical_memory_mb,
    sqlserver_start_time
FROM sys.dm_os_sys_info;

Cauți:

  • Versiunea SQL Server — ideal 2019+.
  • Ediția — Enterprise deblochează mai multe funcționalități.
  • Uptime — dacă < 1 săptămână, wait stats și cache-ul vor induce în eroare.
  • Memoria — compară cu setarea max server memory.

Pasul 2 — sp_Blitz (5 minute)

EXEC sp_Blitz;

Citește constatările de prioritate 1–20. Triaj:

  • P1 „Niciun backup” → repară imediat.
  • P1 „Coruperi” → repară chiar acum, vezi lecția 31.
  • P10 „Parolă slabă cont SA” → notează, ridică la securitate.
  • P50 ciudățenii de configurare → documentează, repară mai târziu.

Pasul 3 — Recovery model și starea backup-urilor (2 minute)

SELECT d.name,
       d.recovery_model_desc,
       DATEDIFF(HOUR, MAX(CASE WHEN bs.type = 'D' THEN bs.backup_finish_date END), GETDATE()) AS hours_since_last_full,
       DATEDIFF(HOUR, MAX(CASE WHEN bs.type = 'L' THEN bs.backup_finish_date END), GETDATE()) AS hours_since_last_log
FROM sys.databases AS d
LEFT JOIN msdb.dbo.backupset AS bs ON bs.database_name = d.name
WHERE d.name NOT IN ('tempdb')
GROUP BY d.name, d.recovery_model_desc;

Cauți:

  • FULL recovery fără backup de log recent → log-ul se umple; repară sau treci pe SIMPLE.
  • SIMPLE recovery pe o bază de date critică → confirmă că e intenționat.
  • Niciun backup full în > 48 de ore → programează unul acum.

Pasul 4 — Configurația tempdb (1 minut)

SELECT name, size * 8 / 1024 AS size_mb, growth, is_percent_growth, type_desc
FROM tempdb.sys.database_files;

Cauți:

  • Un singur fișier de date → adaugă mai multe (unul per vCPU până la 8).
  • is_percent_growth = 1 cu valori mici → schimbă pe MB absoluți.
  • Mărimi mici → pre-crește la o mărime de lucru așteptată.

Pasul 5 — Wait stats (3 minute)

(Interogarea filtrată din lecția 36.) Cauți:

  • PAGEIOLATCH_* în top → disc lent sau buffer pool subdimensionat.
  • LCK_* în top → probleme de blocaj, vezi Pasul 8.
  • CXPACKET în top → probleme de paralelism; probabil neurgent.
  • ASYNC_NETWORK_IO în top → aplicația citește lent sau seturi uriașe de rezultate.
  • SOS_SCHEDULER_YIELD mare → presiune pe CPU.

Top waits-urile definesc următoarea investigație.

Pasul 6 — sp_BlitzFirst pentru wait stats live (2 minute)

EXEC sp_BlitzFirst @ExpertMode = 1;

Eșantion de 5 secunde din activitatea curentă. Îți spune ce se întâmplă chiar acum (vs cumulativ de la repornire).

Pasul 7 — Top interogări scumpe (3 minute)

USE DBA;
EXEC sp_BlitzCache @Top = 20, @SortOrder = 'CPU';
EXEC sp_BlitzCache @Top = 20, @SortOrder = 'Reads';

Citește coloana de warnings. Infractori comuni:

  • Conversie implicită → repară interogarea sau tipul coloanei.
  • Predominanță de key lookup → adaugă un index acoperitor.
  • Parameter sniffing → recompilează sau forțează din Query Store.
  • Spilluri în tempdb → mai multă memorie sau index mai bun.

Pasul 8 — Blocaje curente și activitate live (1 minut)

EXEC sp_WhoIsActive @find_block_leaders = 1, @get_plans = 1;

Cauți:

  • Blocatori cu rulare lungă → decide dacă să-i omori.
  • Sesiuni sleeping cu tranzacții deschise → zombie; omoară.
  • Interogări active grele → corelează cu top CPU.

Pasul 9 — Audit de indecși (5 minute)

USE Runehold;   -- ajustează pentru fiecare bază utilizator
EXEC sp_BlitzIndex;

Cauți:

  • P1 „Indecși duplicat” → șterge duplicatele.
  • Tabele heap → adaugă index clustered.
  • Indecși nefolosiți → candidați la ștergere (verifică în timp).
  • Sugestii de indecși lipsă → evaluează, alege câteva cu impact mare.

Pasul 10 — Postura Query Store (1 minut)

SELECT name, is_query_store_on
FROM sys.databases WHERE name NOT IN ('master', 'tempdb', 'model', 'msdb');

Dacă vreo bază de date utilizator are Query Store oprit, pornește-l (lecția 28). Fără el, nu poți găsi ușor regresii mai târziu.

Pasul 11 — Creșterea fișierelor și utilizarea discului (2 minute)

SELECT DB_NAME(mf.database_id) AS db, mf.name, mf.type_desc,
       mf.size * 8 / 1024 AS size_mb, mf.growth, mf.is_percent_growth
FROM sys.master_files AS mf
WHERE mf.database_id > 4
ORDER BY size_mb DESC;

Cauți:

  • Fișiere de log cu is_percent_growth = 1 → schimbă pe MB absoluți.
  • Creștere fixă mică (de ex. 10 MB) → mărește la o valoare sensibilă.
  • Cele mai mari baze de date → confirmă spațiu de manevră pe disc.

Pasul 12 — Sănătatea joburilor Agent (2 minute)

SELECT TOP (20)
    j.name, h.step_id,
    CASE h.run_status
        WHEN 0 THEN 'Failed' WHEN 1 THEN 'Succeeded' WHEN 2 THEN 'Retry'
        WHEN 3 THEN 'Cancelled' WHEN 4 THEN 'In Progress' END AS status,
    CONVERT(VARCHAR, msdb.dbo.agent_datetime(h.run_date, h.run_time), 120) AS run_time,
    h.message
FROM msdb.dbo.sysjobhistory AS h
JOIN msdb.dbo.sysjobs       AS j ON j.job_id = h.job_id
ORDER BY h.run_date DESC, h.run_time DESC;

Cauți:

  • Eșecuri recente la backup sau joburi de mentenanță.
  • Joburi care n-au mai rulat de mult (program dezactivat? Agent oprit?).
  • Joburi „In Progress” care ar fi trebuit să se termine acum o oră.

Pasul 13 — Verifică prezența Ola Hallengren + First Responder Kit (1 minut)

SELECT OBJECT_SCHEMA_NAME(object_id) + '.' + name AS proc_name
FROM DBA.sys.procedures
WHERE name IN ('IndexOptimize', 'DatabaseBackup', 'DatabaseIntegrityCheck',
               'sp_Blitz', 'sp_BlitzIndex', 'sp_BlitzCache',
               'sp_BlitzFirst', 'sp_WhoIsActive');

Instalează orice lipsește. Minute petrecute foarte bine.

Pasul 14 — Istoric de deadlock-uri (1 minut)

-- Din lecția 20
SELECT TOP (10)
    XEvent.value('(@timestamp)[1]', 'DATETIME2') AS ts,
    XEvent.query('.')                              AS deadlock_xml
FROM (
    SELECT CAST(target_data AS XML) AS td
    FROM sys.dm_xe_session_targets st
    JOIN sys.dm_xe_sessions s ON s.address = st.event_session_address
    WHERE s.name = 'system_health' AND st.target_name = 'ring_buffer'
) AS source
CROSS APPLY td.nodes('RingBufferTarget/event[@name="xml_deadlock_report"]') AS X(XEvent)
ORDER BY ts DESC;

Cauți deadlock-uri recente. Dacă > 1 pe zi, e ceva ce merită rezolvat.

Pasul 15 — Scrie raportul (5 minute)

Ia notițele și transformă-le într-un raport scurt:

Health check SQL Server Runehold — 2026-04-16

  • Per ansamblu: Galben. Trei constatări P1.
  • Critice:
    • BD Sales_archive în recovery FULL fără backup de log de 7 zile. Fișierul de log 87% folosit. Repară azi.
    • Niciun backup pe Reporting_Staging de 48 de ore. Motiv: jobul Agent Stage Backup dezactivat. Reactivează.
  • Mari:
    • tempdb are 1 fișier de date pe un server cu 8 nuclee. Contenție PAGELATCH așteptată. Adaugă încă 7 fișiere.
    • Conversie implicită în top interogarea CPU sp_GetOrdersByCountry. Repară tipul de date.
  • Medii:
    • 22% din indecșii non-clustered nefolosiți de 30+ zile. Evaluează ștergeri.
    • Query Store dezactivat pe 3 baze utilizator. Activează.
  • Mici:
    • Parolă veche sa (politica spune rotație la 90 de zile). Rotește.
    • 140+ rânduri de istoric de joburi Agent mai vechi de un an în msdb. Curăță.
  • De urmărit:
    • Programează un drill trimestrial de restaurare.
    • Instalează scripturile Ola Hallengren.
    • Migrează de la SQL 2019 la 2022 — deja suportat.

Asta e livrabilul tău. Scurt, acționabil, prioritizat.

Ce urmează după acest curs

Acum cunoști echivalentul a un an de experiență la job pe SQL Server, comprimat în 40 de lecții. Ce urmează:

  1. Citește blogul lui Brent Ozar — articole noi săptămânal pe tot ce ține de SQL Server. Cu opinii, practic, amuzant.
  2. Blogul lui Paul Randal la SQLskills — analize tehnice profunde pe motorul de stocare, coruperi și DBCC. Expertul pe internals.
  3. Blogul și videourile lui Erik Darling — sfaturi cu opinie pe tunarea interogărilor, multă magie cu CTE-uri și funcții window.
  4. brentozar.com/training — echipa lui Brent ține cursuri de training. Excelente dacă îți permiți sau dacă plătește angajatorul.
  5. Contribuie la open source — scripturile lui Ola, First Responder Kit, sp_WhoIsActive acceptă pull requests.
  6. Exersează pe o sarcină reală — un sandbox cu Adventure Works sau cu setul tău de date. Rulează health check-ul. Repară constatările. Repetă lunar.

Felicitări

Ai parcurs 40 de lecții de SQL Server. Acum cunoști:

  • Motorul (Modulele 1–3).
  • Indexarea și performanța (Modulele 4–5).
  • Stocarea și partiționarea (Modulul 6).
  • Esențialele DBA — backup-uri, Agent, securitate, tempdb, mentenanță, DMV-uri (Modulul 7).
  • Trusa de urgență — First Responder Kit și sp_WhoIsActive (Modulul 8).

Dacă acest curs și-a făcut treaba, ești gata să deschizi un SQL Server pe care nu l-ai mai văzut, să-l diagnostichezi în 30 de minute, să rezolvi problemele de top și să nu plângi la 3 dimineața când îți dă ping. Asta e ștacheta. Asta înseamnă „să cunoști SQL Server” la acest nivel.

Du-te și ai grijă de sistemele tale de producție. Depind de tine.

— Mulțumesc că ai citit. Dacă ai văzut greșeli, ai sugestii sau vrei să te cerți pe MERGE, salută.

Caută