Index Fragmentation slows down queries measurably because the physical order of the index pages differs from the logical order, which increases I/O, CPU and waiting times. In this guide, I will show you how Reorganization, rebuild, fill factor and monitoring work together to reliably detect and sustainably eliminate fragmentation.
Key points
- DefinitionFragmented B* trees generate more I/O and slower scans.
- Causes: Page splits, deletes, shifted key values.
- ThresholdsReorg from ~5-30 %, rebuild from ~30 %.
- MySQL focusOPTIMIZE TABLE and fill factors.
- AutomationScheduled jobs, online operations, metrics.
What does index fragmentation mean technically?
I refer to as Fragmentation the discrepancy between the logical key sequence and the physical page chain of a B* tree index. Many INSERTs, UPDATEs and DELETEs result in gaps, splits and unordered leaf pages, which trigger more read operations. The result: scans jump more frequently, buffer cache hits decrease and CPU costs increase. Even ideal plans suffer because the memory delivers the scattered pages more slowly. I therefore always pay attention to the context of workload, data size and memory layout.
Types of fragmentation and their symptoms
I make a pragmatic distinction:
- Logical fragmentationThe leaf pages are no longer concatenated in key order. Range scans require additional jumps, read-ahead is less effective.
- Internal fragmentationPages carry a lot of unused space (low fill levels). More pages have to be read per result line; index size increases without benefit.
- Structural fragmentationUnfavorable tree height, unbalanced nodes or heaps with forwarded records (e.g. in SQL Server). Accesses become more indirect.
This can be measured as more pages read per line, higher latencies during range or order-by scans and a falling cache hit rate. I always correlate the signals with wait statistics to avoid confusion with network or storage problems.
Causes: Inserts, updates, page splits
Frequent inserts fill pages right up to the edge, then a new key forces a Page split, which leaves two half-filled pages. Deletes remove entries, but free space remains distributed and is not always used locally with the next insert. Updates that change key columns move records and create more gaps. Randomized key patterns like GUIDs further increase the scattering and thus the clutter. I minimize splits by using the Fill factor to match the write load.
Making performance losses measurable
I do not measure fragmentation in isolation, but in combination with query times, log reads, page reads and wait classes. If the average latency of range scans increases and the CPU per query increases, I first check the physical key figures of the indices. High fragmentation increases the number of pages read per equal number of lines and compresses wait times to I/O. A well-founded comparison before and after reorg or rebuild shows the real benefit. For background information on locking, plans and bottlenecks, it is worth taking a look at Database performance, to classify symptoms correctly.
Metrics, waits and page efficiency in detail
In practice, I also observe:
- Pages per scanHow many leaf pages does a typical area scan read? If the value increases with the same result quantity, this indicates fragmentation or too low fill levels.
- Read-ahead hitFragmented chains sabotage sequential prefetches; the effect is smaller on SSDs, but not zero, as CPU, latches and cache continue to suffer.
- Waiting classesPAGEIOLATCH/IO-Waits (SQL Server), db file sequential/scattered read (Oracle) or increased InnoDB read latencies (MySQL) increase with stronger jumping in the index.
- Cache qualityIf the buffer pool hit rate drops in parallel with fragmentation, a rebuild is almost always worthwhile - especially for large range scans.
Analyze fragmentation: SQL Server, MySQL, Oracle
I always start the analysis with a reliable Snapshot of index health and filter out small indices whose page usage fluctuates statistically. In SQL Server, sys.dm_db_index_physical_stats provides the degree of fragmentation together with page_count so that I can weight outliers. Values above 5-30 % indicate reorganization, strong outliers above 30 % indicate a rebuild, especially with a large page_count. In MySQL, I check SHOW TABLE STATUS or INFORMATION_SCHEMA views and observe data and index length over time. In Oracle, I also check whether an online rebuild is available in order to Downtime to avoid.
Practical queries and weighting
I work with simple, reusable queries and weight them according to page size and relevance:
- SQL ServerI determine the fragmentation and filter out small indices.
SELECT DB_NAME() AS db, OBJECT_NAME(i.object_id) AS obj, i.name AS idx, ips.index_type_desc, ips.page_count, ips.avg_fragmentation_in_percent FROM sys.indexes i CROSS APPLY sys.dm_db_index_physical_stats(DB_ID(), i.object_id, i.index_id, NULL, 'SAMPLED') ips WHERE ips.page_count >= 100 ORDER BY ips.avg_fragmentation_in_percent DESC, ips.page_count DESC; - MySQL (InnoDB)I look at index size, free space and rate of change.
SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE, INDEX_LENGTH, DATA_FREE FROM information_schema.TABLES WHERE ENGINE = 'InnoDB' AND INDEX_LENGTH > 0 ORDER BY (DATA_FREE) DESC;At the same time, I compare the values over time (e.g. daily) to separate real trends from outliers. For statistics, I use ANALYZE TABLE sparingly if the optimizer assumes incorrect cardinalities.
- OracleI check segment statistics (free spaces, extents) and the availability of REBUILD ONLINE in order to keep maintenance windows predictable.
It is important to me to only look at indices with high usage. A fragmented but unused index is more likely to be a candidate for removal than reorganization.
Reorganization vs. rebuild: Decision matrix
I choose the method according to the degree of Fragmentation and operating windows, because not every environment can cope with intensive I/O peaks. Reorganize rearranges leaf pages, reduces logical jumps, compresses to fill factor and usually stays online. Rebuild rebuilds the index, cleans up completely, returns memory and updates statistics, but requires CPU, I/O and often longer locks. Small indexes under about 100 pages rarely benefit greatly, while large structures of 30 % fragmentation or more gain significantly. I document the decision with key figures so that the effect remains comprehensible and the Maintenance schedule fits.
| Method | Resource requirements | Typical use | Main effect |
|---|---|---|---|
| Reorganization | Low to medium | ~5-30 % Fragmentation | Reorganization, compression to fill factor |
| Rebuild | High | > 30 % Fragmentation | Complete rebuild, memory release |
Online options, locks and side effects
For low-interruption operation, I use - where available - Online rebuilds in. I pay attention to this:
- Edition/VersionOnline features vary depending on the database and edition. I check each environment separately.
- Temporary metadata locksEven “online” usually requires blocks at the beginning/end. I deliberately schedule these in quiet phases.
- Temp/working rangesOptions such as SORT_IN_TEMPDB (SQL Server) reduce the load on the main data file, but require additional storage space.
- ReplicationRebuilds increase log volume. I monitor replica lag and throttle if necessary to avoid delays.
For SQL server heaps I take into account Forwarded Records; Here a table rebuild helps to remove redirects. In Oracle, I use REBUILD ONLINE or MOVE PARTITION (with UPDATE INDEXES) to reduce downtime.
Fill factor, page splits and memory
A suitable Fill factor I set between 70-90 % for tables that write a lot, so that future inserts can use free space locally. If I lower the fill factor too much, the index grows faster and takes up more memory; if I set it too high, splits and fragmentation increase. I therefore observe the relationship between page utilization, write load and insert pattern over several cycles. For rebuilds, I deliberately define the fill factor per index, not across the board for the entire database. Regular monitoring prevents an initially good trade-off months later.
Understanding fill factors per platform
- SQL ServerFILLFACTOR is an index property that becomes effective during rebuild/creation. I set a lower value for very volatile secondary indices and a higher value for read-heavy structures. I document the selected value per index and recalibrate after load profile changes.
- MySQL (InnoDB)With innodb_fill_factor I influence the free space that InnoDB leaves for (re)builds. It does not apply to everyday DML, but with OPTIMIZE/ALTER it helps to dampen splits in the future. I also plan hotspots (monotone keys) in such a way that latch competition and splits are reduced.
- Oracle & PostgreSQLSTORAGE parameter or. FILLFACTOR (Postgres) give room for free air in pages. For write-heavy tables, I use conservative fill levels and balance the extra memory with measurably better scan times.
Specific for MySQL and WordPress
In MySQL helps me OPTIMIZE TABLE at InnoDB to reorganize tables and associated indexes and return free space. Highly fragmented workloads with many deletes also benefit from periodic re-creation of critical secondary indexes. In WordPress installations, I reduce clutter such as revisions and spam comments before optimizing so that fewer pages need to be reordered. I combine these steps with a clean index strategy for wp_postmeta and similar tables that often trigger scans. The guide to Optimize WordPress indices, which addresses typical stumbling blocks.
MySQL practice: OPTIMIZE, partitions and side effects
I also pay attention to InnoDB:
- OPTIMIZE TABLE reconstructs the table (and indexes) and can run largely “inplace” depending on the version, but always requires meta locks and log free space. I plan dedicated time windows for this.
- Partitioning allows targeted maintenance: OPTIMIZE PARTITION only for hot or heavily erased areas reduces I/O peaks and runtime.
- ReplicationLarge rebuilds generate binlog volume and can delay replicas. I distribute maintenance over several nights or work in partitions.
- ANALYZE TABLE renews statistics that the Optimizer needs for better plans - especially after massive structural changes.
In WordPress environments, I reduce in advance transients, revisions and deleted posts so that OPTIMIZE moves less data. For wp_postmeta, I check whether queries run specifically via suitable composite indices to avoid broad scans.
PostgreSQL specifics in brief
Even though the focus here is on MySQL, I take heterogeneous environments into account:
- VACUUM/Autovacuum prevents bloat, but does not replace REINDEX if B-tree structures are highly fragmented.
- REINDEX CONCURRENTLY enables new indices to be created largely online with limited blocking.
- fillfactor per table/index controls free air for future INSERTs/UPDATEs. Write-heavy tables benefit from lower values.
- Partitions per period relieve maintenance windows; REINDEX can be used specifically for each partition.
Automated maintenance and threshold values
I automate reorg and rebuild using robust Thresholds and only activate indices with a sufficient page_count to avoid noise. Jobs run in maintenance windows, while I execute long operations via online options with as little downtime as possible. A staggered approach postpones large rebuilds to quiet periods and runs small reorgs more frequently. I update statistics after major changes so that the optimizer can select better plans promptly. Alerts are triggered as soon as fragmentation or latencies exceed predefined limits so that I can act before users complain.
Runbook: Sequence of steps for sustainable results
- IdentifySnapshot of the top N indices by size and fragmentation, filter small indices.
- PrioritizeSort by workload criticality, page_count and scan load.
- PlanningSchedule reorg/rebuild according to threshold values, calculate online options and temp/log requirements.
- PerformStaggering of large objects, I/O throttling, replication lag monitoring.
- StatisticsUpdate statistics after rebuild/OPTIMIZE (or ensure that this is done automatically).
- ValidateMeasure before/after: Latency, pages read, waiting times, cache hit rate.
- CalibrateCheck fill factors and thresholds, document lessons learned.
Hosting tuning: Practical rules
I plan analyses in hosting environments weekly, regulate the I/O window of maintenance and combine with caching to keep hotsets in memory. TempDB/redo/binlog parameters and storage media significantly influence the perceived effects of defragmentation. I also evaluate whether superfluous indexes only generate costs, because every additional index increases write work and the chances of fragmentation. Before each new index, I check workload patterns, cardinalities and existing coverage. I outline typical stumbling blocks in this overview of Index traps in MySQL, which avoids misjudgments.
Costs/benefits and when I consciously do nothing
Not every fragmentation is worth maintaining. I deliberately do without when:
- Object is small (e.g. less than 100 pages) and fluctuates greatly - this is where the benefits fall flat.
- Queries are selective (primarily lookups per key) and no range scans are running.
- Workload is transient (migration window, archiving soon) - then I only plan a final rebuild.
Instead, I invest in better indices, less redundancy and clean key selection so that future splits occur less frequently.
When to reorganize, when to wait?
I solve a Reorganization if the degree of fragmentation increases moderately and enough pages are affected to have a real effect. After mass deletions or archiving, an orderly redistribution often brings noticeable scan gains. In the case of severe outliers or storage requirements, I plan a rebuild, preferably online, so as to have minimal impact on operations. I often leave small indexes of less than 100 pages untouched because their layout fluctuates greatly and the benefits are minimal. I document the decision together with before/after figures so that future cycles are easier to plan.
Long-term prevention through design
Good Scheme design reduces fragmentation even before the first insert by ensuring that key selection, data types and normalization are consistent. I avoid extra-wide rows, which allow fewer data records per page and favor splits. Partitioning separates cold from hot data and reduces side effects during maintenance and backups. Careful query optimization reduces reliance on expensive scans and aligns indexes to real-world patterns. As workloads change, I adjust index definitions incrementally instead of discarding entire structures ad hoc.
Key selection and insert pattern
The choice of primary key has a decisive influence on the split behavior:
- Monotone keys (e.g. AUTO_INCREMENT, time-based IDs) bundle inserts at the right edge, reduce scattering and splits, but can create hotspots. I equalize hotspots with buffering/batching.
- Randomized keys (e.g. GUID/UUID v4) distribute load, but increase split probability. Sequential variants (e.g. time-based UUIDs) balance distribution and order better.
- Wide key increase the index and the number of pages required. Lean, selective keys are more sustainable.
In addition, line and page compression reduces the split rate because there is room for more entries per page. However, I always check CPU costs and license/feature availability before activating compression.
Briefly summarized: Steps with an impact
I start with a focused Analysis of the largest and most fragmented indices, prioritize by page_count and workload criticality. I then implement staggered measures: reorganize moderate cases, rebuild heavy cases, readjust fill factors for each index. Automated jobs maintain order without constant manual intervention, while alerts reliably trigger in the event of outliers. MySQL and WordPress environments benefit noticeably if I reduce data waste beforehand and only retain useful indices. With consistent monitoring, clear thresholds and repeatable playbooks Performance stable - even when the data is growing rapidly.


