Tag: SQL Server Architecture

  • SQL Server Architecture: SQLOS, Buffer Pool, and Plan Cache Explained

    SQL Server Architecture: SQLOS, Buffer Pool, and Plan Cache Explained

    Every performance problem eventually traces back to one of three resources: memory, CPU scheduling, or I/O. Understanding how SQL Server manages all three internally is the foundation this entire course builds on — every later module’s diagnostic technique is really a way of inspecting one of these three subsystems more closely.

    SQL Server Architecture, sketched out(memory, CPU, and I/O — everything traces back here)SQLOS — the scheduler underneath T-SQLmanages CPU threads (schedulers) + memory allocation for everything belowBuffer Poolcached 8KB data pages in RAMread from disk once, RAM afterPlan Cachecached compiled execution plansavoids recompiling same shapeDisk8KB pages on physical storagefirst read onlyNew query shape arrivescompiled once, plan cachedcompile once, reuseMore RAM only helps the BufferPool — CPU scheduling & plancache churn need a different fix. 📌

    SQLOS: The Layer Beneath T-SQL

    SQLOS is SQL Server’s own thin operating-system layer, sitting between the Windows/Linux OS and the relational engine. It manages scheduling (via non-preemptive “SQLOS schedulers” mapped roughly to CPU cores), memory allocation, and synchronization — SQL Server largely manages its own thread scheduling rather than leaving it entirely to the OS, which is why a CPU-bound SQL Server workload behaves differently from a typical application.

    -- See the schedulers directly — one row per logical CPU SQL Server is using
    SELECT scheduler_id, cpu_id, status, is_online, runnable_tasks_count
    FROM sys.dm_os_schedulers
    WHERE status = 'VISIBLE ONLINE';

    A consistently high runnable_tasks_count across schedulers is an early, concrete sign of genuine CPU pressure — more tasks are ready to run than there are schedulers to run them, so they queue.

    The Buffer Pool: Memory’s Biggest Consumer

    Buffer Pool Cached 8KB data pages Read from disk once, served from RAM after A page read from cache is orders of magnitude faster than from disk Plan Cache Cached compiled execution plans Avoids recompiling the same query shape repeatedly First parameter value compiled shapes the cached plan (parameter sniffing)

    Checking Buffer Pool Pressure

    SELECT COUNT(*) * 8 / 1024 AS cached_data_mb
    FROM sys.dm_os_buffer_descriptors;
    
    SELECT total_physical_memory_kb / 1024 AS total_ram_mb,
           available_physical_memory_kb / 1024 AS available_ram_mb
    FROM sys.dm_os_sys_memory;

    Checking Plan Cache Health

    SELECT objtype, COUNT(*) AS plan_count, SUM(CAST(size_in_bytes AS BIGINT))/1024/1024 AS size_mb
    FROM sys.dm_exec_cached_plans
    GROUP BY objtype
    ORDER BY size_mb DESC;

    Why This Matters for Everything Ahead

    When a query is slow, the real question is always: is it waiting on disk I/O because the data wasn’t in the buffer pool? Is it recompiling because plan cache pressure evicted it? Or is CPU scheduling itself the constraint? Every later module — indexing, query optimization, monitoring — is really about managing these same three resources more efficiently.

    Common mistake: Treating “add more RAM” as a universal fix. More RAM only helps the buffer pool side of the equation — it does nothing for CPU scheduling pressure or plan cache churn from constantly-changing query shapes. This exact myth gets its own dedicated debunking in the Bonus module.
    Practice tip: Run the scheduler query above right now, on your own instance, and note the runnable_tasks_count for each scheduler. Come back to this same query after Module 4’s execution plan work and compare it against a query you know is CPU-intensive — seeing the number move is what makes “CPU-bound” a concrete, checkable fact instead of an abstract label.

    Enjoyed this?

    Subscribe to get every new lesson as soon as it’s published, and share it with a developer who’d find it useful.

    📡 Subscribe via RSS  | 
    Share on X  | 
    Share on LinkedIn  | 
    Share on Facebook

    Want the full structured course with quizzes and hands-on labs? Check out SQL Server Performance Tuning, coming soon on this site.

  • SQL Server Page Structure and Transaction Log Architecture Explained

    SQL Server Page Structure and Transaction Log Architecture Explained

    Lesson 1 covered how SQL Server manages memory and CPU. This lesson covers the physical layer underneath both: how data is actually laid out on disk, and how every single change — no matter how it got there — is guaranteed durable before it’s ever considered committed.

    Pages, Extents & the Log, sketched out1 Extent = 8 contiguous pages (64KB)P0P1P2P3P4P5P6P7P3 = 8KB, only ~8060 bytes usableafter the page header + row offsets1. Write to the LOG firstthe change is recorded before anything else moves2. THEN modify the data pagethe actual 8KB page changes on disk3. COMMIT — only once durablethis is literally Durability (the “D” in ACID)A DELETE touching 1,000,000 rows =1,000,000 log records, row-by-row —no matter how fast the disk is. 📌

    Pages and Extents

    One Extent = 8 contiguous pages (64KB) Page 0 Page 1 Page 2 Each page: 8KB, ~8060 bytes usable after header/row offsets A row larger than one page → row-overflow or LOB storage

    This 8KB figure isn’t arbitrary trivia — it’s the exact unit Chapter 4’s STATISTICS IO “logical reads” count is measured in. When that number reports 500 logical reads, it means 500 8KB pages were touched — tying this architecture lesson directly to a diagnostic number you’ll read constantly for the rest of this course.

    The Transaction Log: Write-Ahead Logging

    -- Check log space usage — a classic "why is my log huge" starting point
    DBCC SQLPERF(LOGSPACE);
    
    -- Check log file growth/autogrowth settings
    SELECT name, size/128 AS size_mb, growth, is_percent_growth
    FROM sys.database_files
    WHERE type_desc = 'LOG';

    SQL Server uses Write-Ahead Logging (WAL): a change is written to the transaction log before the data page itself is modified on disk, and a transaction is only considered committed once its log record is durably written. This is literally how Durability (the “D” in ACID from the Developers & DBAs course) is implemented — not a separate feature, but the mechanism underneath it.

    Why Log Architecture Explains Real Symptoms

    A transaction log that won’t shrink, a database stuck in “log full” errors during a bulk load, a mysteriously slow bulk delete — these all trace back to how logging works: every logged operation, including large deletes, must be written to the log before it’s considered durable, row by row. A DELETE affecting a million rows generates roughly a million log records, regardless of how fast the actual data-page changes would otherwise be.

    Common mistake: Assuming a slow DELETE is an indexing problem. If the WHERE clause is already using a good index but the operation still crawls, the transaction log — not the index — is very often the actual bottleneck, because of exactly this row-by-row logging requirement. Understanding this now sets up exactly the diagnostic instinct you’ll need for Module 3’s “13-Hour Delete” case study, where this precise trap is the twist.
    Practice tip: Run DBCC SQLPERF(LOGSPACE) now, note your practice database’s current log size and percent used, then re-run it after inserting a few thousand synthetic rows in one transaction. Watching the log grow in response to a bulk operation, rather than just reading that it does, is what makes Module 3’s case study land correctly when you get there.

    Enjoyed this?

    Subscribe to get every new lesson as soon as it’s published, and share it with a developer who’d find it useful.

    📡 Subscribe via RSS  | 
    Share on X  | 
    Share on LinkedIn  | 
    Share on Facebook

    Want the full structured course with quizzes and hands-on labs? Check out SQL Server Performance Tuning, coming soon on this site.

  • Module 1 Exercises: SQL Server Architecture Labs (10 Hands-On Exercises)

    Module 1 Exercises: SQL Server Architecture Labs

    5 guided labs, 3 challenge scenarios, and 2 break-it labs — applying the Evidence-First workflow to real architecture questions.

    Guided Labs (step-by-step)

    Guided1. Measure your buffer pool’s current cached size and compare it to total server RAM.

    SELECT COUNT(*) * 8 / 1024 AS cached_data_mb FROM sys.dm_os_buffer_descriptors;
    SELECT total_physical_memory_kb/1024 AS total_ram_mb FROM sys.dm_os_sys_memory;
    Guided2. Find the top 5 largest cached execution plans by size and note their objtype.

    SELECT TOP 5 objtype, size_in_bytes/1024 AS size_kb, usecounts
    FROM sys.dm_exec_cached_plans ORDER BY size_in_bytes DESC;
    Guided3. Check your current transaction log size and percent used for a test database.

    DBCC SQLPERF(LOGSPACE);
    Guided4. Create a table, insert 10,000 rows, then check how many pages it occupies.

    CREATE TABLE dbo.PageDemo (id INT IDENTITY PRIMARY KEY, filler CHAR(500));
    INSERT INTO dbo.PageDemo (filler)
    SELECT TOP 10000 REPLICATE('x',500) FROM sys.all_objects a CROSS JOIN sys.all_objects b;
    SELECT page_count, record_count FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('dbo.PageDemo'), NULL, NULL, 'DETAILED');
    Guided5. Run the same parameterized query twice with very different parameter selectivity and compare the cached plan’s row estimates to actual. Use Ctrl+M in SSMS to capture both actual execution plans and note if the estimate matches the second call.

    Challenge Scenarios (diagnose independently)

    Challenge6. A colleague reports the server has 64GB RAM but query performance hasn’t improved since a recent upgrade from 16GB. Using the DMVs from this module, form a hypothesis for why RAM alone didn’t help, and what evidence you’d gather next.
    Challenge7. A transaction log grew from 1GB to 50GB overnight with no obvious large operations in the app. List three architectural causes you’d investigate first, and the exact DMV/DBCC command for each.
    Challenge8. The plan cache shows thousands of single-use ad-hoc plans consuming significant memory. Explain the architectural reason this happens and one setting that mitigates it.

    Break-It Labs

    Break-It9. Deliberately induce plan cache bloat: run 500 slightly-different ad-hoc (non-parameterized) queries in a loop, then measure plan cache growth with the query from Guided Lab 2. Then fix it by enabling “optimize for ad hoc workloads” and re-measure.
    Break-It10. Deliberately induce log growth pressure: run a large batch DELETE inside an explicit transaction without committing for several minutes while inserting more rows elsewhere, then observe DBCC SQLPERF(LOGSPACE) growing. Resolve it by committing and note the log space reclaimed (or not, until a log backup, depending on recovery model).

    Enjoyed this?

    Subscribe to get every new lesson as soon as it’s published, and share it with a developer who’d find it useful.

    📡 Subscribe via RSS  | 
    Share on X  | 
    Share on LinkedIn  | 
    Share on Facebook

    Want the full structured course with quizzes and hands-on labs? Check out SQL Server Performance Tuning, coming soon on this site.

  • Module 1 Quiz: SQL Server Architecture Fundamentals (10 Questions)

    Module 1 Quiz: SQL Server Architecture Fundamentals

    10 questions, mixing conceptual theory with diagnostic interpretation — including a couple of deliberate misconception traps. Try each one yourself before revealing the answer.

    1. What happens to a data page’s copy in the buffer pool after a row on it is updated, before a checkpoint?

    A) Immediately discarded
    B) Marked “dirty” and kept in memory until written to disk
    C) Instantly written to disk
    D) Moved to the plan cache

    Show Answer

    Answer: B

    A modified page in the buffer pool is marked “dirty” and flushed to disk later (checkpoint or lazy writer) — not instantly, which is why the transaction log (not the data file) is what guarantees durability in the meantime.

    2. Which SQL Server component is responsible for its own non-preemptive thread scheduling, largely independent of the OS scheduler?

    A) The buffer pool
    B) SQLOS
    C) The transaction log
    D) tempdb

    Show Answer

    Answer: B

    SQLOS provides its own scheduling layer mapped roughly to CPU cores — this is why SQL Server CPU behavior differs from typical multi-threaded applications.

    3. “Gotcha”: A DBA claims adding more RAM always fixes a slow query. What’s the flaw?

    A) RAM never helps performance
    B) It only helps if the bottleneck is actually buffer pool pressure/disk I/O, not CPU or locking
    C) SQL Server ignores available RAM
    D) More RAM always slows queries down

    Show Answer

    Answer: B

    More RAM grows the buffer pool, which helps only when pages are being evicted and re-read from disk. It does nothing for a CPU-bound or lock-bound query — evidence-first diagnosis tells you which one you actually have.

    4. What determines the execution plan cached for a parameterized query on its first compilation?

    A) The average of all future parameter values
    B) The specific parameter value(s) used at first compilation
    C) SQL Server always recompiles per call
    D) The plan is chosen randomly

    Show Answer

    Answer: B

    This is parameter sniffing — the plan cache reuses the plan compiled for the first parameter value seen, which can be wrong for very differently-shaped later calls.

    5. What is the size of a single SQL Server data page?

    A) 4KB
    B) 8KB
    C) 16KB
    D) 64KB

    Show Answer

    Answer: B

    8KB per page is fixed and hasn’t changed across SQL Server versions; 8 contiguous pages (64KB) form one extent.

    6. What guarantees a committed transaction survives a crash, per the ACID Durability property?

    A) The buffer pool
    B) The plan cache
    C) The transaction log, written before the transaction is considered committed
    D) tempdb

    Show Answer

    Answer: C

    Write-Ahead Logging means the log record is durably written before commit acknowledgment — the data file itself may still be updated later.

    7. “Gotcha”: True or false — a row can never span more than one 8KB page.

    A) True, always
    B) False — row-overflow and LOB storage allow larger rows to span pages

    Show Answer

    Answer: B

    Wide rows (e.g. many VARCHAR(MAX) columns) can exceed one page via row-overflow storage — a common source of unexpected I/O for “wide” tables.

    8. Which DMV shows current buffer pool memory usage by counting cached pages?

    A) sys.dm_os_sys_memory
    B) sys.dm_os_buffer_descriptors
    C) sys.dm_exec_cached_plans
    D) sys.dm_os_wait_stats

    Show Answer

    Answer: B

    sys.dm_os_buffer_descriptors has one row per cached page; sys.dm_os_sys_memory reports overall system memory instead.

    9. A bulk delete on a large table is taking hours and generating huge log growth. What architectural fact explains this?

    A) DELETE doesn’t use the transaction log
    B) Every deleted row is individually logged under Write-Ahead Logging
    C) The buffer pool is too small always
    D) This is unrelated to architecture

    Show Answer

    Answer: B

    Row-by-row logging of a large DELETE is exactly why bulk deletes on huge tables can take hours and balloon the log — this sets up the Module 3 case study.

    10. What does DBCC SQLPERF(LOGSPACE) report?

    A) Buffer pool hit ratio
    B) Plan cache size
    C) Transaction log size and percent used, per database
    D) CPU scheduler queue length

    Show Answer

    Answer: C

    A quick, classic first check when a log is growing unexpectedly or a “log full” error appears.


    Enjoyed this?

    Subscribe to get every new lesson as soon as it’s published, and share it with a developer who’d find it useful.

    📡 Subscribe via RSS  | 
    Share on X  | 
    Share on LinkedIn  | 
    Share on Facebook

    Want the full structured course with quizzes and hands-on labs? Check out SQL Server Performance Tuning, coming soon on this site.