<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>The Lazy DBA</title>
	
	<link>http://www.lazysonofabitch.net</link>
	<description>Helping you do your job with less effort</description>
	<pubDate>Mon, 01 Jun 2009 00:45:11 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/TheLazyDBA" /><feedburner:info uri="thelazydba" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Index Fragmentation: The Silent Killer</title>
		<link>http://feedproxy.google.com/~r/TheLazyDBA/~3/PJ__4sCG-iA/</link>
		<comments>http://www.lazysonofabitch.net/index.php/2009/05/index-fragmentation-the-silent-killer/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 00:02:46 +0000</pubDate>
		<dc:creator>Rob Gomes</dc:creator>
		
		<category><![CDATA[Maintenance]]></category>

		<category><![CDATA[index]]></category>

		<category><![CDATA[script]]></category>

		<guid isPermaLink="false">http://www.lazysonofabitch.net/?p=68</guid>
		<description><![CDATA[
Index fragmentation is a lot like cholesterol.  The bad kind, not the good kind.  It builds up slowly.  Some deletes occur, leaving empty space in a data page here.  Inserts occur, but the target page is packed, so a page split occurs so the record can be inserted in the correct [...]]]></description>
			<content:encoded><![CDATA[<p><img class="size-full wp-image-92 alignright" title="Still has pictures of your mum." src="http://www.lazysonofabitch.net/wp-content/uploads/2009/05/tf2-meet-the-spy-the-silent-killer.jpg" alt="Still has pictures of your mum." width="240" height="240" /></p>
<p>Index fragmentation is a lot like cholesterol.  The bad kind, not the good kind.  It builds up slowly.  Some deletes occur, leaving empty space in a data page here.  Inserts occur, but the target page is packed, so a page split occurs so the record can be inserted in the correct order, yet the other page is now mostly empty.  Updates are a double whammy.  Over time, your index pages continue to be less and less full, meaning you have to perform that many more reads per query.</p>
<p>Just like cholesterol, it&#8217;s not perceptible.  Sure, if you compared yourself now to 10 years ago, you&#8217;d be able to instantly recognize that you feel tired all the time, or occasionally dizzy spells or have blurred vision.  Then all of a sudden &#8212; HEART ATTACK!</p>
<p>Ok, so maybe it&#8217;s not a heart attack.  However your phone is ringing, and your phone <strong><em>never</em></strong> rings.  <span id="more-68"></span>Maybe some queries for your application start timing out, or your users are complaining that Application-X is slow.  However the point remains that the performance degradation over time is imperceptible unless you&#8217;ve set something up to monitor index fragmentation, and you&#8217;re not aware of what was going on until you have a serious problem, and it&#8217;s <strong><em>your</em></strong> problem.</p>
<h3>Doc, What Should I Do?</h3>
<p>Well, your indexes are fragmented, so you should&#8230; defragment them?  See, software is great because unlike high cholesterol, we can just make the problem go away as if we were exercising and eating right for the past decade or so.</p>
<p>For dealing with index fragmentation, we have three possible options:</p>
<ol>
<li>Drop the indexes and recreate them.</li>
<li>Rebuild the index.</li>
<li>Reorganize the index.</li>
</ol>
<p>The first option is great, except indexes that are dropped aren&#8217;t available for use.  However it does ensure that not only will our data pages be as full as possible (or as full as our FILLFACTOR dictates), but they have the best chance of being contiguous, avoiding those pesky disk-head movements.  Additionally, DROP INDEX and CREATE INDEX are atomic, so this means the table isn&#8217;t available for use either while locks are held, not just the index.  It also means that to have all these actions be atomic in aggregate, they&#8217;ll need to be wrapped in a TRANSACTION.  These are also logged operations, which means your transaction log with nom-nom-nom on the hunks of spinning rust in your server.  It&#8217;s great when we have large windows of downtime, but that&#8217;s it.</p>
<p>The second option is to do an ALTER INDEX &#8230; REBUILD.  This dynamically rebuilds the index, and means we don&#8217;t have to worry about PRIMARY KEY or UNIQUE constraints as we would with dropping and recreating the indexes.  The operation is atomic, so we don&#8217;t have to concern ourselves with wrapping it in a transaction either.  Granted, like dropping and recreating the index, locks are created as well &#8212; a shared lock for non-clustered indexes, and an exclusive lock for clustered indexes.  Again, this is also a logged operation.  Enterprise Edition users have the luxury of being able to perform this operation and keep the table online.</p>
<p>Last, we have the lowly ALTER INDEX &#8230; REORGANIZE.  This defragments the leaf level of an index so that the physical order of the pages  matches the left-to-right logical order of the leaf nodes.  It also compacts pages, and removes any empty pages that are created as a result.</p>
<p>Now, <span style="text-decoration: line-through;">standard medical procedure</span> <a title="Technet: Reorganizing and Rebuilding Indexes" href="http://technet.microsoft.com/en-us/library/ms189858.aspx">Microsoft best practices</a> recommend the following as a baseline:</p>
<ul>
<li>If fragmentation is below 5 percent, don&#8217;t bother.</li>
<li>If fragmentation is between 5 and 30 percent, defragment.</li>
<li>If fragmentation is greater than 30 percent, rebuild.</li>
</ul>
<p>Your mileage may vary depending on your application.  How?  We&#8217;ll save that for another day.  Right now, I&#8217;d prefer to prescribe some medication so we can get some immediate results and we can look at the index fragmentation in more detail later.</p>
<h3>Use a Gun.  It That Doesn&#8217;t Work, Use More Gun</h3>
<p>Right now we&#8217;re looking for a general cure so we can act quickly.  Well, step right up, because I have the magic stored procedure that&#8217;ll automagically rebuild or reorganize your indexes, online if supported, put hair on your chest and win you the girl of your dreams!</p>
<pre class="brush: sql">IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N&#039;[dbo].[IndexMaintenance90]&#039;) AND type in (N&#039;P&#039;, N&#039;PC&#039;))
	DROP PROCEDURE [dbo].[IndexMaintenance90]
GO

CREATE PROCEDURE [dbo].[IndexMaintenance90]
	@DefragThreshold							TINYINT = 5
,	@RebuildThreshold							TINYINT = 30
AS
BEGIN
	SET NOCOUNT ON;

	-- Version check.
	IF CAST(LEFT(CAST(SERVERPROPERTY(&#039;ProductVersion&#039;) AS NVARCHAR(128)), 2) AS FLOAT) &lt; 9
		BEGIN
			PRINT &#039;Versions of SQL Server prior to SQL Server 2005 are not supported.&#039;;
			RETURN 0;
		END

	DECLARE @Indexes							TABLE
	(
		[IndexID]								INTEGER	IDENTITY(1, 1)	NOT NULL	PRIMARY KEY CLUSTERED
	,	[SchemaName]							SYSNAME					NOT NULL
	,	[ObjectName]							SYSNAME					NOT NULL
	,	[IndexName]								SYSNAME					NOT NULL
	,	[IndexType]								TINYINT					NOT NULL
	,	[FragmentationPercent]					FLOAT					NOT NULL
	);

	DECLARE @SQLString							NVARCHAR(2048);

	DECLARE	@SchemaName							SYSNAME;
	DECLARE	@ObjectName							SYSNAME;
	DECLARE	@IndexName							SYSNAME;
	DECLARE	@IndexType							TINYINT;
	DECLARE	@FragmentationPercent				FLOAT;
	DECLARE @i									INTEGER;

	INSERT INTO
		@Indexes
	SELECT
		[s].[name]								AS SchemaName
	,	[o].[name]								AS ObjectName
	,	[i].[name]								AS IndexName
	,	[i].[type]								AS IndexType
	,	[ips].[avg_fragmentation_in_percent]	AS FragmentationPercent
	FROM
		[sys].[dm_db_index_physical_stats](DB_ID(), NULL, NULL, NULL, &#039;LIMITED&#039;)	ips
	INNER JOIN
		[sys].[objects]	o
		ON
			[ips].[object_id] = [o].[object_id]
	INNER JOIN
		[sys].[schemas]	s
		ON
			[o].[schema_id] = [s].[schema_id]
	INNER JOIN
		[sys].[indexes]	i
		ON
			[ips].[object_id] = [i].[object_id]
		AND	[ips].[index_id] = [i].[index_id]
	WHERE
		[i].[type] &gt; 0
	AND	[i].[is_disabled] = 0
	AND [ips].fragment_count IS NOT NULL
	AND	[ips].[avg_fragmentation_in_percent] &gt; ISNULL(@DefragThreshold, 0);

	SELECT @i = MIN([i].[IndexID]) FROM @Indexes i;

	SELECT
		@SchemaName				= [i].[SchemaName]
	,	@ObjectName				= [i].[ObjectName]
	,	@IndexName				= [i].[IndexName]
	,	@IndexType				= [i].[IndexType]
	,	@FragmentationPercent	= [i].[FragmentationPercent]
	FROM
		@Indexes	i
	WHERE
		[i].[IndexID] = @i;

	WHILE @@ROWCOUNT &gt; 0
	BEGIN
		SET @SQLString = &#039;ALTER INDEX &#039; + QUOTENAME(@IndexName) + &#039; ON &#039; + QUOTENAME(@SchemaName) + &#039;.&#039; + QUOTENAME(@ObjectName);

		IF @FragmentationPercent &gt;= ISNULL(@RebuildThreshold, 0)
			BEGIN
				-- Enterprise Editions support online rebuilding.  However, XML indexes can&#039;t be rebuilt online.
				IF (SERVERPROPERTY(&#039;EngineEdition&#039;) = 3) AND @IndexType &lt;&gt; 3
					SET @SQLString = @SQLString + &#039;REBUILD WITH (ONLINE = ON);&#039;
				ELSE
					SET @SQLString = @SQLString + &#039;REBUILD;&#039;
			END
		ELSE
			BEGIN
				SET @SQLString = @SQLString + &#039; REORGANIZE;&#039;;
				-- XML Indexes do not have statistics to update.
				IF @IndexType &lt;&gt; 3
					SET @SQLString = @SQLString + CHAR(10) + &#039;UPDATE STATISTICS &#039; + QUOTENAME(@SchemaName) + &#039;.&#039; + QUOTENAME(@ObjectName) + &#039; &#039; + QUOTENAME(@IndexName) + &#039;;&#039;;
			END	

		PRINT @SQLString;
		EXECUTE sp_executesql @SQLString;

		SELECT @i = MIN([i].[IndexID]) FROM @Indexes i WHERE [i].[IndexID] &gt; @i;

		SELECT
			@SchemaName				= [i].[SchemaName]
		,	@ObjectName				= [i].[ObjectName]
		,	@IndexName				= [i].[IndexName]
		,	@IndexType				= [i].[IndexType]
		,	@FragmentationPercent	= [i].[FragmentationPercent]
		FROM
			@Indexes	i
		WHERE
			[i].[IndexID] = @i;
	END

	PRINT CHAR(10) + &#039;Maintenance complete.&#039;;
	RETURN 0;
END
GO</pre>
<pre>IndexMaintenance90
  [@DefragThreshhold = ] <em>defrag_threshold<span style="font-style: normal;">,
  [@RebuildThreshhold = ] <em>rebuild_threshold</em></span></em></pre>
<p>[<strong>@DefragThreshold</strong> = ] <em>defrag_threshold</em><br />
<strong>TINYINT</strong>.  The minimum percentage of fragmentation in which an index should be defragmented, with a default of <strong>5</strong>.</p>
<p>[<strong>@RebuildThreshold</strong> = ] <em>rebuild_threshold</em><br />
<strong>TINYINT</strong>.  The minimum percentage of fragmentation in which the index will be rebuilt instead of defragmented, with a default of <strong>30</strong>.</p>
<p>This mamma jamma will determine the level of fragmentation for all indexes in the current database, and rebuild, defrag or do nothing as appropriate, keeping your maintenance windows to a minimum.  If your server is running the Enterprise Edition of the engine, it&#8217;ll perform an online rebuild.  Indexes that are reorganized also have their statistics updated.  Currently this doesn&#8217;t support partitioned indexes, but it shall in a future version.</p>
<p>Just create it and go &#8212; either fire it off manually, or create a SQL Agent Job to run this as part of your regular maintenance.</p>
<p><strong>Download</strong>: <a href="http://www.lazysonofabitch.net/wp-content/uploads/2009/05/indexmaintenance90.sql">(SQL Server 2005/2008) IndexMaintenance90.sql</a></p>
<img src="http://feeds.feedburner.com/~r/TheLazyDBA/~4/PJ__4sCG-iA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.lazysonofabitch.net/index.php/2009/05/index-fragmentation-the-silent-killer/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.lazysonofabitch.net/index.php/2009/05/index-fragmentation-the-silent-killer/</feedburner:origLink></item>
		<item>
		<title>Hello, world!</title>
		<link>http://feedproxy.google.com/~r/TheLazyDBA/~3/q6csbxXgLmk/</link>
		<comments>http://www.lazysonofabitch.net/index.php/2009/05/hello-world/#comments</comments>
		<pubDate>Sat, 09 May 2009 01:29:55 +0000</pubDate>
		<dc:creator>Rob Gomes</dc:creator>
		
		<category><![CDATA[Whatever]]></category>

		<category><![CDATA[blogging]]></category>

		<guid isPermaLink="false">http://www.lazysonofabitch.net/?p=38</guid>
		<description><![CDATA[They say that the best way to find success is to find someone who is successful, and to use them as a model.  So yeah, I&#8217;m blogging, despite the fact that there&#8217;s millions of blowhards that all think that they too are some unique and special snowflake.
Like most bloggers, I have some selfish reasons which [...]]]></description>
			<content:encoded><![CDATA[<p>They say that the best way to find success is to find someone who is successful, and to use them as a model.  So yeah, I&#8217;m blogging, despite the fact that there&#8217;s millions of blowhards that all think that they too are some unique and special snowflake.</p>
<p><img class="alignleft size-full wp-image-37" title="Your typical technology blogger" src="http://www.lazysonofabitch.net/wp-content/uploads/2009/05/king_size_homer_at_home.jpg" alt="Your typical technology blogger" width="240" height="200" />Like most bloggers, I have some selfish reasons which prompted the decision, and the primary reason I&#8217;ve decided to give it a shot is for my own professional development.</p>
<p>There are folks in every community for every technology that have thousands of regular visitors to their interweb soapbox.  Granted, they have their audience for good reasons: they&#8217;re great speakers, have excellent breadth and depth of knowledge, and they provide insightful perspective on problems they&#8217;ve experienced that their audience regularly encounters.</p>
<p>In short: <strong>They provide value.</strong></p>
<p>To be honest, I originally just intended on changing my email hosting provider, though I decided that an earnest attempt at blogging might not be a terri-bad idea.   I&#8217;m always struggling to find some outlet in which to entertain myself.  I also occasionally solve difficult problems in an elegant way, and it&#8217;d be great if I had an always accessible reference to the solutions wherever I go.  I&#8217;m detail-oriented to a fault, and love to inject my <em>special</em> sense of humor into things, so I figure it couldn&#8217;t hurt to make it available to the world in case anyone else was interested.</p>
<p>So I guess this is the point where we watch me post a few piddly articles and then abruptly fade in oblivion, as it is fashionable for one to do on their Spacebook or Myface page, right?  Hey, that&#8217;s more common than not!  If it happens I&#8217;m trying to avoid breaking the blogger mold.  That&#8217;s my story and I&#8217;m sticking to it!</p>
<img src="http://feeds.feedburner.com/~r/TheLazyDBA/~4/q6csbxXgLmk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.lazysonofabitch.net/index.php/2009/05/hello-world/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.lazysonofabitch.net/index.php/2009/05/hello-world/</feedburner:origLink></item>
	</channel>
</rss>

