The following example uses ALTER TABLE to reorganize the clustered index on the sales_big table: ALTER INDEX ci_sales_big on sales_big REORGANIZE After running this command, you can run
Trang 1Listing 34.6 shows examples of running sys.dm_db_index_physical_stats on the
sales_big table, using both LIMITED and DETAILED scan modes
LISTING 34.6 sys.dm_db_index_physical_stats Examples
use bigpubs2008
go
select str(index_id,3,0) as indid,
left(index_type_desc, 20) as index_type_desc,
index_depth as idx_depth,
index_level as idx_level,
str(avg_fragmentation_in_percent, 5,2) as avg_frgmnt_pct,
str(page_count, 10,0) as pg_cnt
FROM sys.dm_db_index_physical_stats
(db_id(), object_id(‘sales_big’),null, 0, ‘LIMITED’)
select str(index_id,3,0) as indid,
left(index_type_desc, 20) as index_type_desc,
index_depth as idx_depth,
index_level as idx_level,
str(avg_fragmentation_in_percent, 5,2) as avg_frgmnt_pct,
str(page_count, 10,0) as pg_cnt
FROM sys.dm_db_index_physical_stats
(db_id(), object_id(‘sales_big’),null, 0, ‘DETAILED’)
go
indid index_type_desc idx_depth idx_level avg_frgmnt_pct pg_cnt
- - -
-1 CLUSTERED INDEX 3 0 63.42 -145 -19
2 NONCLUSTERED INDEX 3 0 14.90 4571
indid index_type_desc idx_depth idx_level avg_frgmnt_pct pg_cnt - - -
-1 CLUSTERED INDEX 3 0 63.42 -145 -19
1 CLUSTERED INDEX 3 1 92.11 38
1 CLUSTERED INDEX 3 2 0.00 1
2 NONCLUSTERED INDEX 3 0 14.90 4571
2 NONCLUSTERED INDEX 3 1 87.50 8
2 NONCLUSTERED INDEX 3 2 0.00 1
Again, you can see from the output in Listing 34.6 that the logical fragmentation
(avg_frgmnt_pct) is 63.42% for the leaf level of the clustered index (idx_level = 0) This
indicates that nearly two thirds of the data pages are out of sequence in relation to
Trang 2ing of the clustered key values If you want to improve the performance of table scans or
clustered index scans for the sales_big table, you need to decide whether to rebuild the
index or simply defragment the index
The degree of fragmentation helps you decide which defragmentation method to use A
rough guideline to use to help decide is to examine the avg_fragmentation_in_percent
value returned by the sys.dm_db_index_physical_stats function If the
avg_fragmentation_in_percent value is greater than 5% but less than 30%, you should
reorganize the index If the avg_fragmentation_in_percent value is greater than 30%, you
should consider rebuilding the index If you also have a dedicated maintenance window
large enough to perform a rebuild instead of simply reorganizing the index, you may as
well run a rebuild because it performs a more thorough defragmentation than
reorganiz-ing the index
Another factor in determining whether an index needs to be defragmented is how the
data is accessed If your applications are performing primarily single-row lookups,
randomly accessing individual rows of data, the internal or external fragmentation is not a
factor when it comes to query performance Accessing one row from a fragmented table is
just as easy as from an unfragmented table However, if your applications are performing
ordered range scan operations and reading all or large numbers of the pages in a table,
excessive fragmentation can greatly slow down the scan The more contiguous and full the
pages, the better the performance will be of the scanning operations
TIP
If you have very low levels of fragmentation (less than 5%), it is recommended that you
not bother with either a reorganization or a rebuild because the benefit of removing
such a small amount of fragmentation is not enough to justify the cost of reorganizing
or rebuilding the index
In SQL Server 2008, the ALTER INDEX command provides options for defragmenting an
index Following is the syntax for the ALTER INDEX command:
ALTER INDEX { index_name | ALL }
ON [ database_name [ schema_name ] | schema_name ]
table_or_view_name
{ REBUILD
[ [PARTITION = ALL]
[ WITH ( <rebuild_index_option> [ , n ] ) ]
| [ PARTITION = partition_number
[ WITH ( <single_partition_rebuild_index_option>
[ , n ] )
] ]
]
| DISABLE
Trang 3| REORGANIZE
[ PARTITION = partition_number ]
[ WITH ( LOB_COMPACTION = { ON | OFF } ) ]
| SET ( <set_index_option> [ , n ] )
}
[ ; ]
<rebuild_index_option > ::=
{
PAD_INDEX = { ON | OFF }
| FILLFACTOR = fillfactor
| SORT_IN_TEMPDB = { ON | OFF }
| IGNORE_DUP_KEY = { ON | OFF }
| STATISTICS_NORECOMPUTE = { ON | OFF }
| ONLINE = { ON | OFF }
| ALLOW_ROW_LOCKS = { ON | OFF }
| ALLOW_PAGE_LOCKS = { ON | OFF }
| MAXDOP = max_degree_of_parallelism
| DATA_COMPRESSION = { NONE | ROW | PAGE }
[ ON PARTITIONS ( { <partition_number_expression> | <range> }
[ , n ] ) ]
}
<range> ::=
<partition_number_expression> TO <partition_number_expression>
}
<single_partition_rebuild_index_option> ::=
{
SORT_IN_TEMPDB = { ON | OFF }
| MAXDOP = max_degree_of_parallelism
| DATA_COMPRESSION = { NONE | ROW | PAGE } }
}
<set_index_option>::=
{
ALLOW_ROW_LOCKS = { ON | OFF }
| ALLOW_PAGE_LOCKS = { ON | OFF }
| IGNORE_DUP_KEY = { ON | OFF }
| STATISTICS_NORECOMPUTE = { ON | OFF }
}
The REORGANIZE option is always performed online, regardless of which edition of SQL
Server 2008 you are running, allowing for other users to continue to update and query
the underlying data in the table while the REORGANIZE process is running The REBUILD
option can be executed online only if you are running SQL Server 2008 Enterprise or
Developer Editions In all other editions of SQL Server 2008, the REBUILD option is
Trang 4executed offline When it is executed offline, SQL Server acquires exclusive locks on the
underlying data and associated indexes so any data modifications to the table are blocked
until the rebuild completes
Reorganizing an index uses minimal system resources to defragment only the leaf level of
clustered and nonclustered indexes of tables and views The first phase of the
reorganiza-tion process compacts the rows on the leaf pages, reapplying the current fill factor value
to reduce the internal fragmentation To view the current fill factor setting, you can run a
query such as the following against the sys.indexes system catalog view:
select cast(name as varchar(30)) as name, index_id, fill_factor
from sys.indexes
where object_id = object_id(‘sales_big’)
go
name index_id fill_factor
-
-ci_sales_big 1 0
idx1 2 0
For more information on fill factor and how to set it, see the “Setting the Fill Factor”
section, later in this chapter
The second phase of the reorganization process involves the rearranging of the leaf-level
pages so that the logical and physical order of the pages match, thereby reducing the
external fragmentation of the leaf level of the index SQL Server 2008 runs a
REORGANIZATION of an index online because the second phase processes only two pages at
a time, in an operation similar to a bubble sort When defragmenting the index, SQL
Server 2008 determines the first physical page belonging to the leaf level and the first
logical page in the leaf level, and it swaps the data on those two pages It then identifies
the next logical and physical page and swaps them, and so on, until no more swaps need
to be made At this point, the logical page ordering matches the physical page ordering
While swapping the logical and physical pages, SQL Server uses an additional new page as
a temporary storage area After each page swap, SQL Server releases all locks and latches
and saves the key of the last moved page
The following example uses ALTER TABLE to reorganize the clustered index on the
sales_big table:
ALTER INDEX ci_sales_big on sales_big REORGANIZE
After running this command, you can run a query similar to the query in Listing 34.6 to
display the fragmentation of the ci_sales_big index on the sales_big table:
select str(s.index_id,3,0) as indid,
left(i.name, 20) as index_name,
left(index_type_desc, 20) as index_type_desc,
index_depth as idx_depth,
index_level as level,
str(avg_fragmentation_in_percent, 5,2) as avg_frgmnt_pct,
Trang 5str(page_count, 10,0) as pg_cnt
FROM sys.dm_db_index_physical_stats
(db_id(‘bigpubs2008’), object_id(‘sales_big’),1, 0, ‘DETAILED’) s
join sys.indexes i on s.object_id = i.object_id and s.index_id = i.index_id
go
indid index_name index_type_desc idx_depth level avg_frgmnt_pct pg_cnt
- - - -
-1 ci_sales_big CLUSTERED INDEX 3 0 0.32 -145 -19
1 ci_sales_big CLUSTERED INDEX 3 1 92.11 38
1 ci_sales_big CLUSTERED INDEX 3 2 0.00 1
As you can see, the average fragmentation percentage is down to 32% from 63.42%, indi-cating that the index is now mostly defragmented However, the average fragmentation percentage of the intermediate level of the index (level = 1) is still 92.11%, indicating that it is heavily fragmented To defragment the nonleaf levels of the index, you need to rebuild the index The following example shows how to rebuild the index using the ALTER INDEX command: ALTER INDEX ci_sales_big on sales_big REBUILD After running this command, you can again run a query similar to the query in Listing 34.6 to display the fragmentation of the ci_sales_big index on the sales_big table: select str(s.index_id,3,0) as indid, left(i.name, 20) as index_name, left(index_type_desc, 20) as index_type_desc, index_depth as idx_depth, index_level as level, str(avg_fragmentation_in_percent, 5,2) as avg_frgmnt_pct, str(page_count, 10,0) as pg_cnt FROM sys.dm_db_index_physical_stats (db_id(‘bigpubs2008’), object_id(‘sales_big’),1, 0, ‘DETAILED’) s join sys.indexes i on s.object_id = i.object_id and s.index_id = i.index_id go indid index_name index_type_desc idx_depth level avg_frgmnt_pct pg_cnt - - - - -
-1 ci_sales_big CLUSTERED INDEX 3 0 0.0 -1 -14520
1 ci_sales_big CLUSTERED INDEX 3 1 5.26 38
1 ci_sales_big CLUSTERED INDEX 3 2 0.00 1
You can see from these results that the REBUILD option performs a more thorough
defrag-mentation of the ci_sales_big index than REORGANIZE The average fragmentation
percentage of both the leaf and intermediate levels is significantly less
Trang 6NOTE
When you rebuild a nonclustered index, the rebuild operation requires enough
tempo-rary disk space to store both the old and new indexes However, if the index is disabled
before being rebuilt, the disk space made available by disabling the index can be
reused by the subsequent rebuild or any other operation No additional space is
required except for temporary disk space for sorting, which is typically only about 20%
of the index size
Therefore, if disk space is limited, it may be helpful to disable a nonclustered index
before rebuilding it For more information on disabling indexes, see the “Disabling
Indexes” section, later in this chapter
One of the other options to the CREATE INDEX and ALTER INDEX commands is the
FILLFACTOR option The fill factor allows you to specify, as a percentage, the fullness of the
pages at the data and leaf index page levels, essentially deciding how much free space to
create in the index and data pages to make room for new rows and avoid page splits
Setting the Fill Factor
Fill factor is a setting you can use when creating an index to specify, as a percentage, how
full you want your data pages or leaf-level index pages to be when the index is created A
lower fill factor has the effect of spreading the data and leaf index rows across a greater
number of pages by leaving more free space in the pages This reduces page splitting and
dynamic reorganization of index and data pages, which can improve performance in
envi-ronments where there are a lot of inserts and updates to the data, while at the same time
reducing performance for queries because an increased number of pages need to be read to
retrieve multiple rows A higher fill factor has the effect of packing more data and index
rows per page by leaving less free space in the pages Using a higher fill factor is useful in
environments where the data is relatively static because it reduces the number of pages
required for storing the data and its indexes, and it helps improve performance for queries
by reducing the number of pages that need to be accessed
By default, when you create an index on a table, if you don’t specify a value for
FILLFACTOR, the default value is 0 With a FILLFACTOR setting of 0, or 100, the data pages
for a clustered index and the leaf pages for a nonclustered index are created completely
full However, space is left within the nonleaf nodes of the index for one or two more
rows The default fill factor to be used when creating indexes is a server-level
configura-tion opconfigura-tion If you want to change the server-wide default for the fill factor, you use the
sp_configure command:
sp_configure ‘fill factor’,N
It is generally recommended that you leave the server-wide default for fill factor as 0
and specify your FILLFACTOR settings on an index-by-index basis You can specify a
Trang 7specific fill factor value for an index by including the FILLFACTOR option for the CREATE
INDEX statement:
CREATE [UNIQUE] [CLUSTERED | NONCLUSTERED] INDEX index_name
ON [ [database_name.][schema_name.]] table_or_view_name
[ WITH ( <relational_index_option> [ , n ] ) ]
<relational_index_option> ::=
{ PAD_INDEX = { ON | OFF }
| FILLFACTOR = fillfactor
| SORT_IN_TEMPDB = { ON | OFF }
| IGNORE_DUP_KEY = { ON | OFF }
| STATISTICS_NORECOMPUTE = { ON | OFF }
| DROP_EXISTING = { ON | OFF }
| ONLINE = { ON | OFF }
| ALLOW_ROW_LOCKS = { ON | OFF }
| ALLOW_PAGE_LOCKS = { ON | OFF }
| MAXDOP = max_degree_of_parallelism }
The FILLFACTOR option for the CREATE INDEX command allows you to specify, as a
percentage, how full the data or leaf-level index pages should be when you create an
index on a table The specified percentage can be from 1 to 100 Specifying a value of 80
would mean that each data or leaf page would be filled approximately 80% full at the
time you create the index It is important to note that as more data gets modified or
added to a table, the fill factor is not maintained at the level specified during the CREATE
INDEX command Over a period of time, you will find that each page has a different
percentage of fullness as rows are added and deleted
TIP
A fill factor setting specified when creating a nonclustered index affects only the
non-clustered index pages and doesn’t affect the data pages To apply a fill factor to the
data pages in a table, you must provide a fill factor setting when creating a clustered
index on the table Also, it is important to remember that the fill factor is applied only
at index creation time and is not maintained by SQL Server When you begin updating
and inserting data, the fill factor is eventually lost Therefore, specifying a fill factor
when creating your indexes is useful only if the table already contains data or if you
simply want to set a default fill factor for the index other than 0 that will be used when
indexes are rebuilt or reorganized by ALTER INDEX
If you specify only the FILLFACTOR option, only the data or leaf-level index pages are
affected by the fill factor To specify the level of fullness for nonleaf pages, use the PAD_INDEX
option together with FILLFACTOR This option allows you to specify how much space to
leave open on each node of the index, which can help to reduce page splits within the
Trang 8nonleaf levels of the index You don’t specify a value for PAD_INDEX; it uses the same
percentage value specified with the FILLFACTOR option For example, to apply a 50% fill
factor to the leaf and nonleaf pages in a nonclustered index on title_id in the titles
table, you would execute the following:
CREATE INDEX title_id_index on titles (title_id)
with (FILLFACTOR = 50, PAD_INDEX = ON)
TIP
When you use PAD_INDEX, the value specified by FILLFACTOR cannot be such that the
number of rows on each index node falls below two If you do specify such a value,
SQL Server internally overrides it so that the number of rows on an intermediate index
page is never less than two
Reapplying the Fill Factor
When might you need to reestablish the fill factor for your indexes or data? As data gets
modified in a table, the value of FILLFACTOR is not maintained at the level specified in the
CREATE INDEX statement As a result, each page can reach a different level of fullness Over
a period of time, this can lead to heavy fragmentation in the database if insert/delete
activity is not evenly spread throughout the table, and it could affect performance In
addition, if a table becomes very large and then very small, rows could become isolated
within data pages This space will likely not be recovered until the last row on the page is
deleted and the page is marked as unused To either spread out rows or to reclaim space by
repacking more rows per page, you need to reapply the fill factor to your clustered and
nonclustered indexes
In environments where insert activity is heavy, reapplying a low fill factor might help
performance by spreading out the data and leaving free space on the pages, which helps
to minimize page splits and possible page-locking contention during heavy OLTP activity
You can use Performance Monitor to monitor your system and determine whether
exces-sive page splits are occurring (See Chapter 39, “Monitoring SQL Server Performance” for
more information on using Performance Monitor.)
A DBA must manually reapply the fill factor to improve the performance of the system
This can be done by using the ALTER INDEX command discussed earlier or by dropping
and re-creating the index ALTER INDEX is preferred because, by default, it applies the
origi-nal fill factor specified when the index was created, or you can provide a new fill factor to
override the default The original fill factor for an index is stored in sys.indexes in the
fill_factor column In addition, if you use the ALTER INDEX command to reorganize or
rebuild your table or index, it attempts to reapply the index’s original fill factor when it
reorganizes the pages
Trang 9Disabling Indexes
Another feature available in SQL Server 2008 is the capability to set an index as disabled
When an index is disabled, the definition of the index is maintained in the system catalogs,
but the index itself contains no index key rows Disabling an index prevents user access to
the index Disabling a clustered index also prevents access to the underlying table data
You can manually disable an index at any time by using the ALTER INDEX DISABLE statement:
ALTER INDEX titleidind ON sales DISABLE
The reasons you might want to disable an index include the following:
Correcting a disk I/O or allocation error on an index page and then rebuilding the
index later
Temporarily removing the index for troubleshooting purposes
Saving temporary disk space while rebuilding nonclustered indexes
When you disable an index, the index is not maintained while it is disabled, and the
Query Optimizer does not consider the index when creating query execution plans
However, statistics on a disabled nonclustered index remain in place and are updated
automatically if the AutoStats option is in effect
If you disable a clustered index, all nonclustered indexes on the table are automatically
disabled as well The nonclustered index cannot be re-enabled until the clustered index is
either enabled or dropped After you enable the clustered index, the nonclustered indexes
must be explicitly enabled unless the clustered index was enabled by using the ALTER
INDEX ALL REBUILD statement Because the data rows of the table cannot be accessed while
the clustered index is disabled, the following operations cannot be performed on the table:
SELECT, UPDATE, DELETE, and INSERT
CREATE INDEX
CREATE STATISTICS
UPDATE STATISTICS
ALTER TABLE statements that modify table columns or constraints
After an index is disabled, it remains in a disabled state until it is rebuilt or dropped You
can enable a disabled index by rebuilding it by using one of the following methods:
ALTER INDEX statement with the REBUILD clause
CREATE INDEX with the DROP_EXISTING clause
DBCC DBREINDEX
To determine whether an index is currently disabled, you can use the INDEXPROPERTY
func-tion (a value of 1 indicates the index is disabled):
select indexproperty(object_id(‘sales’), ‘titleidind’, ‘IsDisabled’)
-1
Trang 10FIGURE 34.27 Setting and viewing index properties in SSMS
Managing Indexes with SSMS
So far, you’ve seen the commands necessary for index management In addition to these
commands, SSMS provides tools for managing indexes
To reorganize or rebuild an index using SSMS, in the Object Explorer, connect to an
instance of the SQL Server 2008 Database Engine and then expand that instance Then
expand Databases, expand the database that contains the table with the specified index,
and expand Tables Next, expand the table in which the index belongs and then expand
Indexes Finally, right-click the index to rebuild and then click Rebuild or Reorganize To
rebuild or reorganize all indexes on a table, right-click Indexes and select Rebuild All or
Reorganize All
You can also disable indexes in SSMS In the Object Explorer, right-click the index you
want to disable and then select the Disable option To disable all indexes on a table,
right-click on Indexes and select Disable All
You can also use SSMS to modify indexes In the Object Explorer, right-click the index you
want to modify and then click Properties In the Properties dialog that appears (see Figure
34.27), you can add or remove columns from the index, change the uniqueness setting,
set the index option, set the fill factor, rebuild the index, view the index fragmentation,
reorganize the index, and so on