I’m in the middle of a flight from Washington D.C. to Zurich on the way to Barcelona for TechEd IT Forum and I can’t sleep – Kimberly’s out like a light so what else is there to do except write another blog post? OK - actually posting this from Barcelona on Tuesday before our first of 12 sessions here!
In the Database Maintenance workshop we did at SQL Connections last week I promised to blog a script I used to show how data file shrink operations cause massive fragmentation of indexes. The reason is that data file shrink starts at the end of the data file and moves a single page at a time to a free space below the shrink threshold. In the process of doing so, it perfectly reverses the physical order of the pages comprising the leaf level of an index – thus perfectly fragmenting it!!
Let’s try out my simple script that demonstrates this. First thing I’m going to do is create a new database and create a 10MB ‘filler’ table, which I’m going to then drop later to create a space that shrink can use.
USE MASTER; GO IF DATABASEPROPERTYEX ('shrinktest', 'Version') > 0 DROP DATABASE shrinktest; CREATE DATABASE shrinktest; GO USE shrinktest; GO SET NOCOUNT ON; GO -- Create and fill the filler table CREATE TABLE filler (c1 INT IDENTITY, c2 VARCHAR(8000)) GO DECLARE @a INT; SELECT @a = 1; WHILE (@a < 1280) -- insert 10MB BEGIN INSERT INTO filler VALUES (REPLICATE ('a', 5000)); SELECT @a = @a + 1; END; GO
USE MASTER;
GO
IF DATABASEPROPERTYEX ('shrinktest', 'Version') > 0
DROP DATABASE shrinktest;
CREATE DATABASE shrinktest;
USE shrinktest;
SET NOCOUNT ON;
-- Create and fill the filler table
CREATE TABLE filler (c1 INT IDENTITY, c2 VARCHAR(8000))
DECLARE @a INT;
SELECT @a = 1;
WHILE (@a < 1280) -- insert 10MB
BEGIN
INSERT INTO filler VALUES (REPLICATE ('a', 5000));
SELECT @a = @a + 1;
END;
Next I’ll create the ‘production’ table that we’d really like to keep in optimal shape for performance.
-- Create and fill the production table CREATE TABLE production (c1 INT IDENTITY, c2 VARCHAR (8000)); CREATE CLUSTERED INDEX prod_cl ON production (c1); GO DECLARE @a INT; SELECT @a = 1; WHILE (@a < 1280) -- insert 10MB BEGIN INSERT INTO production VALUES (REPLICATE ('a', 5000)); SELECT @a = @a + 1; END; GO
-- Create and fill the production table
CREATE TABLE production (c1 INT IDENTITY, c2 VARCHAR (8000));
CREATE CLUSTERED INDEX prod_cl ON production (c1);
INSERT INTO production VALUES (REPLICATE ('a', 5000));
Now I’ll use the sys.dm_db_index_physical_stats DMV to check the fragmentation of the production table’s clustered index – it should be almost zero:
-- check the fragmentation of the production table SELECT avg_fragmentation_in_percent, fragment_count FROM sys.dm_db_index_physical_stats ( DB_ID ('shrinktest'), OBJECT_ID ('production'), 1, NULL, 'LIMITED'); GO avg_fragmentation_in_percent fragment_count ---------------------------- -------------------- 0.390930414386239 6
-- check the fragmentation of the production table
SELECT avg_fragmentation_in_percent, fragment_count FROM sys.dm_db_index_physical_stats (
DB_ID ('shrinktest'), OBJECT_ID ('production'), 1, NULL, 'LIMITED');
avg_fragmentation_in_percent fragment_count
---------------------------- --------------------
0.390930414386239 6
This is what I expected. Now I’m going to drop the filler table, run a shrink operation and then check the fragmentation again:
-- drop the filler table and shrink the database DROP TABLE filler; GO -- shrink the database DBCC SHRINKDATABASE (shrinktest); GO -- check the index fragmentation again SELECT avg_fragmentation_in_percent, fragment_count FROM sys.dm_db_index_physical_stats ( DB_ID ('shrinktest'), OBJECT_ID ('production'), 1, NULL, 'LIMITED'); GO avg_fragmentation_in_percent fragment_count ---------------------------- -------------------- 99.7654417513683 1277
-- drop the filler table and shrink the database
DROP TABLE filler;
-- shrink the database
DBCC SHRINKDATABASE (shrinktest);
-- check the index fragmentation again
99.7654417513683 1277
Wow! The index went from almost 0% fragmented to almost 100% fragmented – the shrink operation totally reversed the physical ordering of the leaf level of the clustered index – nasty.
One of the common maintenance operations I see at customer sites is to run a database shrink at some interval, and I always advise against it – now you can see why. Running a regular shrink operation can cause horrible fragmentation problems. The worst problems I see are those customers with maintenance plans that rebuild all indexes and then run a shrink to remove the extra space necessary for the index rebuilds – completely undoing the effects of the index rebuild!
One other common thing I see is to have auto-shrink set on for one or databases. This is bad for several reasons:
Bottom-line: auto-shrink should *NEVER* be turned on…
Remember Me
a@href@title, strike
Theme design by Jelle Druyts
Pick a theme: BlogXP sqlx BlogXP sqlx
Powered by: newtelligence dasBlog 2.0.7226.0
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.
© Copyright 2008, Paul S. Randal
E-mail