Who Cares about FillFactor?

,

Introduction

In my career as software engineer and neophyte DBA, I have spent countless

hours performance tuning SQL Server and troubleshooting production bottlenecks.

I have had great success in hunting down deadlock culprits and in achieving

massive performance gains on individual stored procedures. But even in spite of

my successes, I have to admit, I have been overlooking one of the most basic

performance tuning techniques in the book – proper configuration of Index

FillFactors.

What is an indexes “FillFactor”

An indexes fillfactor specifies the percentage that the index data pages on

disk are filled when the index is first created. An index fillfactor of 100%

will cause each index data page to be completely filled up. This is ideal from a

disk capacity standpoint as there is no wasted space with data pages that are

not fully allocated. However, this is not ideal from a SQL Server performance

perspective regarding data updates (Inserts, Updates and Deletes). If you create

a clustered index that has a fillfactor of 100%, every time a record is

inserted, deleted or even modified, page splits can occur because there is

likely no room on the existing index data page to write the change. Page splits

increase IO and can dramatically degrade SQL Server performance.

It is very easy to react by simply applying a very low fillfactor of say,

50%, to reduce page splits in a highly transactional system. The problem with

this approach is that by doing this, you have in affect doubled the amount of

data required to read and cache index information for a table. So in improving

write performance, you have potentially degraded read performance. The trick is

to find the right balance between read performance and write performance by

optimizing the fillfactor settings for each index.

Specifying the fillfactor percentage

When creating indexes in SQL Server, by default, if you do not specify an

index fillfactor, the fillfactor will be 0 (effectively the same as 100%). You

can specify an indexes fillfactor percentage in a number of different ways. Some

of these methods include: The Create Index statement, DBCC DBReindex and

database maintenance plans.

Create Index

The Create Index statement in SQL Server will allow you to specify a

fillfactor setting upon index creation. The following example creates a

clustered index with a fillfactor of 80%.

CREATE CLUSTERED INDEX [ci_Product_ProductId] 
ON [dbo].[Product]([ProductId])
WITH FILLFACTOR=80
ON [PRIMARY]

DROP_EXISTING

An optimization that was newly introduced in SQL Server 2000 is the

DROP_EXISTING option in the CREATE Index statement. As part of database

maintenance, it is fairly common to write scripts that drop all indexes and then

recreate them as a means of reindexing the data and reestablishing fillfactor

settings (hence freeing up page space that has been consumed since specifying

the original fillfactor property).

Reindexing is time consuming and expensive because table locks are held while

indexing is taking place. Furthermore, dropping and recreating a clustered index

forces all the nonclustered indexes for that table to be reindexed as well. The

DROP_EXISTING clause rids us of the need for separate DROP INDEX and CREATE

INDEX pairs. Using the DROP_EXISTING option in the CREATE INDEX statement keeps

the nonclustered indexes from being rebuilt. This reduces the time required to

reindex, reduces IO and reduces unnecessary locks on the table in question.

DBCC DBReindex

The DBCC DBReindex command can be used in SQL Server to force Reindexing.

This command can be executed for a single index or for an entire table. This

example reindexes the index named ‘UPKCL_auidind’ in the Authors database and

sets an index fillfactor of 80%: 

DBCC DBREINDEX ('pubs.dbo.authors', UPKCL_auidind, 80)

This example rebuilds all indexes on the Authors table using a fillfactor

value of 70%:

DBCC DBREINDEX (authors, '', 70)

Database Maintenance Plans

In SQL Server, database maintenance plans can be used to manage nightly

backups, integrity checks and other miscellaneous DBA chores. One of the options

available in Database Maintenance Plan creation is ‘Reorganize data and index

pages’. With this option, you can indicate for SQL Server to change the free

space percentage for all index pages. For example, setting the free page

percentage to 10% creates fillfactors of 90% for all indexes in the database.

Although this may not be the best way to optimize all indexes in a system, it

certainly is a start.

PAD_INDEX

Another index setting to be aware of is the ‘Pad_Index’ setting. When

specifying a fillfactor of 90% for an index, only the leaf level pages of the

index are originally created with 10% free space. All non-leaf level pages are

filled to the maximum level. By specifying the PAD_INDEX argument, you ensure

that non-leaf level pages are also padded with the fillfactor setting. This

example creates a clustered index with a fillfactor of 80% using the PAD_INDEX

option:

CREATE CLUSTERED INDEX [ci_Product_ProductId] 
ON [dbo].[Product]([ProductId])
WITH PAD_INDEX, FILLFACTOR=80
ON [PRIMARY]

Are My Indexes Optimized?

Once you know how an indexes fillfactor can impact your server’s performance,

the next step is to perform analysis on your database to determine if your index

fillfactor settings are properly configured.

Analysis tools

SQL Server and Windows both provide an excellent set of tools to monitor IO

performance and to indicate if fillfactor settings could benefit from

optimization.

DBCC Showcontig

The first tool is to have SQL Server show the percentage of contiguous disk

spaced used by individual indexes by using the DBCC Showcontig utility. If

Indexes are written to contiguous pages on disk, then when reading the data, SQL

Server can use random access mode to read the data into memory. If the data is

on non-contiguous pages, SQL Server may have to resort to sequential access to

read the data. Sequential access is much slower than random access for reading

data from disk. DBCC Showcontig can be executed against the entire database, an

entire table within the database, or against a specific index within a table

(See Books On Line For specifics).

The following Example executes DBCC Showcontig for all indexes in the Employees

table:

USE Northwind
GO
DBCC SHOWCONTIG (Employees)
GO

The following script will execute DBCC Showcontig for the index named

‘PK_AKA’ in the AKAs Table:

declare @id int
Declare @indexId int
Declare @IndexName varchar(128)
SET @IndexName = 'PK_AKA'
SET @ID = OBJECT_ID('AKAs')
select @IndexID = indId from sysindexes where id = @id and name = @indexName
dbcc showcontig(@id,@indexID)

The output of DBCC Showcontig provides a plethora of useful information about

the index, including the level of fragmentation for the index data pages, the

size of the index itself (number of pages allocated for it), and the average

percentage each page is full (correlated to the fillfactor settings).

Here is the output of the above script, before and after defragging our indexes

on this database. (Prior to database reorganization, our index fillfactor was

set to 100% although that is not immediately apparent based on this data).

Before Database Reorganization

DBCC SHOWCONTIG scanning 'AKAs' table...
Table: 'AKAs' (1977058079); index ID: 1, database ID: 5
TABLE level scan performed.
- Pages Scanned................................: 1702
- Extents Scanned..............................: 239
- Extent Switches..............................: 1699
- Avg. Pages per Extent........................: 7.1
- Scan Density [Best Count:Actual Count].......: 12.53% [213:1700]
- Logical Scan Fragmentation ..................: 50.00%
- Extent Scan Fragmentation ...................: 99.58%
- Avg. Bytes Free per Page.....................: 2604.3
- Avg. Page Density (full).....................: 67.82%
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

After Database reorganization

DBCC SHOWCONTIG scanning 'AKAs' table...
Table: 'AKAs' (1977058079); index ID: 1, database ID: 5
TABLE level scan performed.
- Pages Scanned................................: 1281
- Extents Scanned..............................: 161
- Extent Switches..............................: 160
- Avg. Pages per Extent........................: 8.0
- Scan Density [Best Count:Actual Count].......: 100.00% [161:161]
- Logical Scan Fragmentation ..................: 0.00%
- Extent Scan Fragmentation ...................: 0.00%
- Avg. Bytes Free per Page.....................: 790.4
- Avg. Page Density (full).....................: 90.23%
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
The results speak wonders to the importance of index maintenance and proper fillfactor settings.

Result Summary:

Pages Scanned

The pages scanned value indicates the number of pages allocated to the index.

Notice, in our example, that prior to database reorganization, this index

spanned 1,702 pages. After database reorganization, the index only spanned 1,281

pages.

Extents Scanned

The number of extents scanned reports the number of different extents the above

listed indexes are written to on disk. Ideally, this number should be as low as

possible. Prior to our database maintenance, the extents scanned value was 239.

After maintenance was performed, this number dropped to 161. This indicates that

we dramatically reduced the fragmentation of our indexes on disk by moving them

to contiguous disk space.

Extent Switches

Number of times the DBCC statement moved from one extent to another while it

traversed the pages of the table or index. Ideally this number should be as

close as possible to the total number of extents scanned. If this number is

higher than the number of extents scanned, it would indicate that the indexes

are fragmented. Notice that, prior to maintenance, our extent switches were

1,699 compared to our extents scanned value of 239 (highly fragmented). After

maintenance, our extent switches equaled 160 where as our extents scanned value

equaled 161 (zero fragmentation).

Avg Pages per Extent

The number of pages per extent in the index page chain. The higher the number

the less fragmented the indexes are. Our statistics show 7.1 compared to 8.0

before and after database maintenance.

Scan Density

This level indicates the level of fragmentation in the index. 100% indicates

100% density, or NO Fragmentation. Notice, again, that prior to maintenance we

were showing 12.53% Density Vs. 100% after maintenance - obviously a huge

improvement.

Avg Page Density (full)

This number indicates, on average, how full the pages are for the index in

question. In our example, you will notice that prior to defragging our database,

the number was 67.82% and after defragging our database (and setting fillfactor

to 90%), our average page density is 90.23%

Perfmon Counters

Performance Monitor is another invaluable tool for monitoring disk IO and

index issues. Three key counters to watch when analyzing index fillfactor

settings are discussed below:

SQL Server Access Methods: Page Splits\Sec

This counter monitors the number of page splits that are occurring in SQL Server

every second. Obviously, you want this number to be as low as possible. A high

number indicates a potential need for reducing your fillfactor percentages. An

excessively low number could mean that your fillfactor percentages are actually

TOO low. Remember, low fillfactor percentages increase the size of the database,

and increase the amount of data required by the SQL Query engine to read indexes

and table data.

Physical Disk: % Disk Read Time

This counter shows the % of disk time for read operations.

Physical Disk: % Disk Write Time

This counter shows the % of disk time for write operations.

Using the Read and Write time counters in unison will provide a good picture of

how read and write intensive your database is. Remember, in a read only

environment a fillfactor of 100% is probably appropriate, whereas in a write

intensive environment a much lower fillfactor is likely to pay dividends.

Rules of Thumb

So what should your fillfactors be set to? That is a difficult question to

answer. I believe that performance should be tightly monitored in a production

environment and indexes should be fine tuned as performance shows that changes

are necessary. The following numbers are recommended as a general starting

point:

  • If your database is read only (low number of updates), use a fillfactor

    of 100%.

  • If your database is write intensive (writes greatly exceed reads), use a

    fill factor somewhere between 50% and 70%.

  • If your database is both read and write intensive, start with a

    fillfactor of 80% to 90%.

  • As you can see, these settings depend on the use of the table. If a

    specific table is only used for lookups, then its clustered index can have a

    fillfactor of 100% even in an update intensive environment. Furthermore, for

    indexes that consume fewer than 100 pages, the fillfactor settings are

    likely to show negligible results.

Index Maintenance

Finally, index fillfactors are not automatically maintained by SQL Server.

This means that when you create the index, the fillfactor setting is honored,

but as data changes in the database, the pages will eventually fill up. I

recommend that you have a maintenance plan that reindexes on a scheduled basis

(nightly or weekly for example, depending on your needs).

Summary

When troubleshooting database performance problems, even the most careful

scrutiny of stored procedures, index placement and database blocking can be

overshadowed by incorrect fillfactor settings. Paying attention to this one

simple index configuration option can significantly increase your database

performance by dramatically reducing disk IO.

Rate

4.71 (14)

Share

Share

Rate

4.71 (14)