SQLServerCentral Article

Index Fragmentation Explained: Page Splits, Logical Reads, and What to Do

,

If you have ever stared at a query that used to run in two seconds and now takes hours, while nothing obvious has changed; no schema updates, no new code, no server migration,- fragmentation can be one of the first area you should check. It does not announce itself. It builds quietly, page by page, as your data grows and changes, and by the time it shows up in your execution times, it has usually been accumulating for weeks or months.

This guide will discuss what index fragmentation is at the storage level, how to measure it, what it does to 'read performance' over time, and how to build a maintenance approach that targets the right indexes rather than blindly rebuilding everything on a schedule.

SQL Server stores every index as a B-tree - a balanced tree structure where the leaf level holds the actual data pages. Each page is 8KB. Under healthy conditions, those pages sit in a logical, ordered sequence that mirrors the physical order on disk. When you query a range of rows, the engine reads those pages in sequence. That's fast.

Fragmentation breaks that orderly arrangement. It's not a bug or a misconfiguration; it's the direct result of your data changing. Every INSERT, UPDATE, and DELETE reshapes the physical layout of your index pages over time. On any table with meaningful write activity, fragmentation is not a risk. It's a certainty.

There are two types of fragmentation:

I. External (logical) Fragmentation - When the physical order of pages on disk no longer matches the logical order of the index keys. In this case, the index still works but the pages it needs are scattered rather than contiguous. The main driver here is the page split.

When you insert a new row into a page that's already full, SQL Server can't append it. It creates a new page, moves roughly half the existing rows to that new page, and inserts the new row in the right position. That new page gets allocated wherever space is available. You do this thousands of times on a busy table, and the index pages end up physically scattered across the disk, even though the logical key order is maintained.

II. Internal (page density) Fragmentation - Here, the pages aren't out of order, they're just not full. DELETE operations are the main cause. When you delete rows, SQL Server marks those slots as available but doesn't compact the page or return unused space. The page density drops, the page count stays the same, and you end up reading more pages than the actual data volume requires.

How to Measure Fragmentation Before You Do Anything

The biggest mistake in fragmentation management is skipping measurement and going straight to rebuilding. Before touching anything, you need to know what you're dealing with. The right tool is sys.dm_db_index_physical_stats.

Here's the query I ran against a test Orders table:

SELECT
    i.name AS IndexName,
    ips.avg_fragmentation_in_percent,
    ips.avg_page_space_used_in_percent,
    ips.page_count
FROM sys.dm_db_index_physical_stats(
    DB_ID(), OBJECT_ID('dbo.Orders'), NULL, NULL, 'DETAILED') ips
JOIN sys.indexes i
    ON i.object_id = ips.object_id
    AND i.index_id = ips.index_id
ORDER BY ips.avg_fragmentation_in_percent DESC;

After running ~200,000 mixed inserts, updates, and deletes on the table without any maintenance, here's what came back:

Index NameAvg Fragmentation (%)Avg Page Space Used (%)Page Count
PK_Orders_OrderID67.8448.2114,302
IX_Orders_CustomerID43.1761.458,914
IX_Orders_OrderDate28.6374.185,208

The clustered index PK_Orders_OrderID showed 67.84% external fragmentation and only 48% page density. Both types of fragmentation had compounded on the same index.

The avg_page_space_used_in_percent column tells you about internal fragmentation. You want that number high. The avg_fragmentation_in_percent column tells you about external fragmentation. You want that number low.

One thing worth noting: always check page_count before drawing conclusions. Fragmentation on an index with 200 pages is essentially noise. The engine scans the whole thing regardless. Fragmentation matters when you're dealing with indexes spanning thousands of pages. As a general rule, indexes with fewer than 1,000 pages aren't worth the maintenance overhead, no matter what the fragmentation percentage shows.

What Fragmentation Does to Read Performance

The most direct way to see fragmentation's impact is through logical reads. The number of 8KB pages the engine accesses to satisfy a query. I measured this with SET STATISTICS IO ON before and after maintenance.

On the test Orders table, a date range query covering 90 days produced these results:

Before maintenance (67% fragmentation, 48% page density):

Table 'Orders'. Scan count 1, logical reads 28,419, physical reads 214

After REBUILD:

Table 'Orders'. Scan count 1, logical reads 11,847, physical reads 0

That's around 16,500 fewer logical reads on a single query, not because the data changed, but because the pages are now compact and ordered. Those extra reads draw down your buffer pool with pages that are only half useful, and they show up directly in execution time under concurrent load.

There's a second effect worth knowing. When fragmentation is high and page density is low, the query optimizer sometimes abandons an index seek in favor of a full clustered index scan which means that jumping between scattered pages costs more than a sequential read. This plan change is hard to diagnose because the index exists and the statistics are current. The actual cause is the physical state of the index, which isn't visible without explicitly checking fragmentation levels.

REBUILD vs. REORGANIZE

Once you know that fragmentation is large enough, you have two options.

ALTER INDEX REBUILD drops the index and recreates it from scratch. The result is near-zero fragmentation, full page density at the FILLFACTOR you specify, and updated statistics as a byproduct. The downside is that an offline rebuild holds a schema modification lock on the table for the duration, on a large table, that means minutes of blocking. You can fix this with REBUILD WITH (ONLINE = ON) but it requires the Enterprise Edition.

ALTER INDEX REORGANIZE defragments the existing index in place, page by page, compacting and reordering as it goes. It's always online, never blocks reads or writes, but it doesn't update statistics and is less effective against severe fragmentation.

The most common thresholds - REORGANIZE between 10–30%, REBUILD above 30%, are a better starting point. A 35% fragmented index with 8,000 pages on a high-traffic table is worth a REBUILD in a low-traffic window. A 40% fragmented index with 600 pages on a reporting table queried twice a day, probably isn't.

When Maintenance Can Make Performance Worse

Rebuilding every index in a large database every night, regardless of fragmentation level, can create I/O storms during the maintenance window. If that window is not long enough, or if the database is larger than anticipated, the rebuild jobs will bleed into business hours and compete with production queries for I/O throughput.

Using auto-shrink can also lead to performance issues. If Auto-shrink is enabled on your database and it runs after an index rebuild, it will shrink the data files, forcing SQL Server to reallocate pages and re-fragment the indexes you just rebuilt. You should disable it on production databases. The fragmentation it causes will outweigh any disk space it recovers.

Another common mistake is rebuilding small indexes repeatedly. Every rebuild operation has overhead: it uses CPU, generates transaction log activity, and, for offline rebuilds, holds locks. Running this against indexes with a few hundred pages, night after night, produces no measurable read performance improvement and burns resources that could serve production queries instead.

How to Build a Maintenance Strategy That Works

Before your maintenance job decides what to do, it should query sys.dm_db_index_physical_stats and make decisions based on current fragmentation levels rather than assuming every index needs attention.

The logic should work roughly like this: skip any index with fewer than 1,000 pages, REORGANIZE indexes between 10-30% fragmentation, and REBUILD indexes above 30%. This adaptive approach means your maintenance window is focused on where it produces results.

Ola Hallengren's SQL Server Maintenance Solution is the most widely adopted open-source implementation of this pattern in the MSSQL community. It handles adaptive index maintenance, statistics updates, and integrity checks, and it has been production-tested across thousands of environments. If you are building a maintenance strategy from scratch, it is a practical starting point rather than something to reinvent.

Beyond the maintenance schedule, also look at your FILLFACTOR setting. FILLFACTOR controls how full SQL Server packs index pages when it builds or rebuilds an index. A FILLFACTOR of 100 fills every page completely, which is efficient for read-only or insert-only tables but accelerates page splits on tables with mixed write patterns. Setting FILLFACTOR to 80-90 on write-heavy tables leaves room on each page for future inserts, reducing the frequency of page splits and slowing the rate at which fragmentation accumulates between maintenance cycles.

Addressing Root Causes of the Problem

Maintenance only manages fragmentation. It does not eliminate the conditions that produce it. If a table accumulates severe fragmentation within days of a rebuild, that is a signal worth investigating rather than a reason to rebuild more frequently.

The most common structural cause of rapid fragmentation is a poor clustered index key choice. Random GUIDs as clustered keys are the classic example. Because new rows do not arrive in key order, nearly every insert causes a page split. Over time, the fragmentation rate on a GUID-keyed table is significantly higher than on a table keyed by a sequential integer or an identity column.

If you are using GUIDs for uniqueness but suffering from fragmentation, NEWSEQUENTIALID() is worth considering as an alternative to NEWID(). It generates GUIDs in an ascending sequence, which eliminates the random insertion pattern that drives page splits without requiring you to abandon GUIDs entirely.

When fragmentation is a symptom of schema design rather than write volume alone, no amount of index maintenance will solve the underlying problem. You need to recognize the difference between a maintenance problem and a design problem. That ability separates reactive database administration from deliberate performance work. Fragmentation does not exist in isolation; it sits alongside query tuning, statistics management, and execution plan analysis as one piece of a larger picture. If you are dealing with persistent performance issues that rebuilding indexes can't fix alone, a structured approach to MSSQL database optimization can be useful.

Rate

You rated this post out of 5. Change rating

Share

Share

Rate

You rated this post out of 5. Change rating