<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	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"
	>

<channel>
	<title>Ajmer Dhariwal's SQL Server blog</title>
	<atom:link href="http://www.eraofdata.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.eraofdata.com/blog</link>
	<description>SQL Server posts that DBAs will (hopefully) find useful.</description>
	<pubDate>Wed, 17 Feb 2010 09:21:00 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.1</generator>
	<language>en</language>
			<item>
		<title>Misaligned disk partition offsets and SQL Server Performance</title>
		<link>http://www.eraofdata.com/blog/2010/02/misaligned-disk-partition-offsets-and-sql-server-performance/</link>
		<comments>http://www.eraofdata.com/blog/2010/02/misaligned-disk-partition-offsets-and-sql-server-performance/#comments</comments>
		<pubDate>Tue, 09 Feb 2010 20:07:07 +0000</pubDate>
		<dc:creator>Ajmer Dhariwal</dc:creator>
		
		<category><![CDATA[SQL Server]]></category>

		<category><![CDATA[partition alignment]]></category>

		<category><![CDATA[Partition offsets]]></category>

		<category><![CDATA[SQL Server IO performance]]></category>

		<guid isPermaLink="false">http://www.eraofdata.com/blog/?p=221</guid>
		<description><![CDATA[I&#8217;m starting off this series of posts with a discussion about partition offsets. Diving off at the deep end a bit perhaps, but if your disk setup is not based on firm foundations, you&#8217;re setting yourself up for one of the most common and easily-avoidable performance issues right from the off, and a IO performance [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m starting off this series of posts with a discussion about partition offsets. Diving off at the deep end a bit perhaps, but if your disk setup is not based on firm foundations, you&#8217;re setting yourself up for one of the most common and easily-avoidable performance issues right from the off, and a IO performance hit of 20-30%.</p>
<p>The partition offset issue is relevant to any disk partitions created <em>prior</em> to Windows Server 2008. What this means is that whilst your OS may be Windows Server 2008, if the disk you are using was migrated from an older OS the chances are that it may still be exposed to the problem.<br />
Also, certain OEM built machines may be exposed to this problem even on Windows Server 2008 as they often include hidden partitions used for e.g. server recovery date.</p>
<h4>Partition offset</h4>
<p>Without going into too great a level of detail on how a disk is structured both physically and logically, the partition offset is basically where Windows will start writing its first bytes to disk once the partition concerned is first used.<br />
When a disk partition is created in Windows Server 2003 and before, the first 63 sectors are reserved for the MBR as well as disk vendor proprietary information. That means Windows Server 2003 and before (hereon referred to as just Windows) will write its first bytes of data on the 64th sector.</p>
<h4>What&#8217;s the big deal?</h4>
<p>These 63 sectors are invariably 512 bytes (worth checking with your SAN team/disk vendor however as some SANs may use larger sector size but present the sectors as logical 512 bytes sectors to Windows). Therefore the 64th sector will begin at a 31.5KB sector boundary. What this means is that if this is a SQL Server disk, SQL Server will write its first data page on a 31.5K sector boundary, and every <em>n</em>th page will be written across these boundaries.<br />
The big deal therefore is that each of these <em>n</em>th IO operations will result in an additional IO to read or write the data that was on the page that crossed that boundary (the actual value of <em>n</em> will depend on cluster (sector) size and stripe unit size).<br />
What this means in real (performance) terms is that a properly aligned system will yield a 20-30% performance improvement in IO latency (and therefore query execution time). All this for free, without having to analyse SQL Server configuration, indexing strategy, query plans etc etc.</p>
<h4>How do I check my offset?</h4>
<p>Checking the offset can be done via a wmi console (wmic) command.</p>
<table border="0">
<tbody>
<tr>
<td><span style="font-size: small; background-color: #000000; font-family: &quot;Courier New&quot;; color: #ffffff;"> wmic /node: &lt;name of server to check&gt; partition get BlockSize, StartingOffset, Name, Index</p>
<p></span></td>
</tr>
</tbody>
</table>
<p>The above command will show output similar to the following (if the <code>wmic</code> command is being executed locally, there&#8217;s no need for the /node parameter):</p>
<table border="0">
<tbody>
<tr>
<td bgcolor="black"><span style="font-size: small; color: #ffffff;"><br />
C:\WINDOWS\system32&gt;</span><span style="font-size: small; background-color: #000000; font-family: &quot;Courier New&quot;; color: #ffff00;">wmic /node:myDbServer partition get BlockSize, StartingOffset, Name, Index<br />
<span style="font-size: small; color: #ffffff;"><br />
BlockSize  Index  Name                    StartingOffset<br />
512        0      Disk #0, Partition #0   32256<br />
512        0      Disk #1, Partition #0   32768<br />
512        0      Disk #2, Partition #0   32768<br />
512        0      Disk #3, Partition #0   32256<br />
512        0      Disk #4, Partition #0   32256<br />
512        0      Disk #5, Partition #0   1048576<br />
512        0      Disk #6, Partition #0   1048576<br />
</span></span></td>
</tr>
</tbody>
</table>
<p>There will be one line of output for each disk and each disk will be identified by its disk number.<br />
Use the Disk Administrator to identify how these drive letters map to your drive letters.<br />
Surprisingly enough, the StartingOffset column is the key column, and will show the starting offset in bytes.<br />
Disks 1 and 2 have 64 sector offset which lines up with e.g. legacy controllers on local disks.<br />
Drives on default values will show a value of 32256, such as disks 0, 3 and 4 in the output above. Divide that by the 512 byte sectors and you&#8217;ll get that dreaded value of 63.<br />
Disks 5 and 6 have a 1MB offset which should line up with most modern disk, SAN cache and cluster size scenarios.<br />
What the above output shows is that, depending on the age of the system, there can be a myriad of offsets used which were correct according to how the sytem was configured at the time. Disks 1 and 2 were altered from the default, so at some point in this system&#8217;s history someone looked into the issue of partition misalignment and tweaked it for this drive.<br />
So why are disks 3 and 4 on misaligned defaults? Well, have you ever had disks recut to (usually) increase storage. That usually involves blowing away partitions and recreating them. If partition offsetting was not carried out when the partition was created for whatever reason, this is what you can end up with. The newest disks 5 and 6 were created with the latest information to hand, hence the higher offset, but as this slightly contrived example shows (it&#8217;s based on real world systems) it pays to be vigilant and review systems that were previously correctly set up.</p>
<h4>How do I fix it?</h4>
<p>On paper, the fix is very simple; adjust the partition offset. The <code>diskpart</code> command line utility is used to reset the offset.<br />
Move the offset along from the default misaligned 31.5K offset to an offset that works with your cluster size and stripe unit size; 64K, or a multiple of 64K usually works on most systems, but where a SAN is involved, it gets more complicated as stripe unit sizes will vary from vendor to vendor and rig to rig, so you&#8217;ll have to get your SAN engineer/vendor involved.<br />
Windows Server 2008 defaults to 1024K offset.</p>
<p>Ideally, the offset needs to start at a point where the IO requests that SQL Server issues (which will be a multiple of 8K, depending on the IO request type being fulfilled), the cluster size, and any caching involved at disk controller/SAN level all line up, thus avoiding additional IO requests.<br />
The downside, of course, is the partition will have to be recreated, so downtime will be involved on standalone servers that have no HA solution implemented.</p>
<p>Realigning the partition offset involves blowing away the disk partition in question, so if you&#8217;re not familiar with <code>diskpart</code>, leave it to your relevant storage/Windows team to reset the partition. If you really want to do this yourself, you&#8217;ll find step-by-step instructions in the SQLCAT team&#8217;s <em>Disk Partition Alignment Best Practices</em> article linked to at the bottom of this post.</p>
<p>The resulting 20-30% improvement in latency times have been verified by the SQLCAT team who have published them in that paper on partition alignment mentioned above, and is highly recommended reading for anyone with an interest in this subject.</p>
<h5>Links</h5>
<p>If you only read one further article on partition offsets, make it this one!<br />
<a href="http://msdn.microsoft.com/en-us/library/dd758814.aspx" target="_blank">Disk Partition Alignment Best Practices for SQL Server</a></p>
<p><a href="http://technet.microsoft.com/en-us/library/cc966500.aspx" target="_blank">SQL Server 2000 I/O Basics</a></p>
<p><a href="http://support.microsoft.com/kb/929491" target="_blank">Disk performance may be slower than expected when you use multiple disks in Windows Server 2003, in Windows XP, and in Windows 2000</a></p>
<p><script type="text/javascript"><!--
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
// --></script><br />
<script type="text/javascript"><!--
var pageTracker = _gat._getTracker("UA-5764551-1");
pageTracker._trackPageview();
// --></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eraofdata.com/blog/2010/02/misaligned-disk-partition-offsets-and-sql-server-performance/feed/</wfw:commentRss>
		</item>
		<item>
		<title>SQL Server and Disk IO</title>
		<link>http://www.eraofdata.com/blog/2010/02/sql-server-and-disk-io/</link>
		<comments>http://www.eraofdata.com/blog/2010/02/sql-server-and-disk-io/#comments</comments>
		<pubDate>Tue, 09 Feb 2010 20:07:00 +0000</pubDate>
		<dc:creator>Ajmer Dhariwal</dc:creator>
		
		<category><![CDATA[SQL Server]]></category>

		<category><![CDATA[Disk performance]]></category>

		<category><![CDATA[IO]]></category>

		<guid isPermaLink="false">http://www.eraofdata.com/blog/?p=211</guid>
		<description><![CDATA[Many aspects of a database system&#8217;s configuration will affect the performance of queries running on that DBMS. However, there is one single component that has the greatest impact on DBMS performance, and that, of course, is disk (IO) access.
It&#8217;s a broad topic and all aspects of it are pretty well covered in various blogs and [...]]]></description>
			<content:encoded><![CDATA[<p>Many aspects of a database system&#8217;s configuration will affect the performance of queries running on that DBMS. However, there is one single component that has the greatest impact on DBMS performance, and that, of course, is disk (IO) access.</p>
<p>It&#8217;s a broad topic and all aspects of it are pretty well covered in various blogs and technical articles, but there&#8217;s not much out there that gathers it all together. The intention of this series of posts is to try consolidate some of this information and focus in on some of the functions and procedures that can be used to monitor, troubleshoot and configure SQL Server disk configuration and most, importantly, disk performance. It won&#8217;t be exhaustive and cover every conceivable option, but will be based on experience gained whilst working at Sybase and Microsoft and sprinkling of &#8216;real world&#8217; database administration experience.</p>
<p>I&#8217;ve split the topic up into smaller sub-topics in order to make it more manageable:</p>
<p>1. <a href="http://www.eraofdata.com/blog/2010/02/misaligned-disk-partition-offsets-and-sql-server-performance">Partition offsets</a><br />
2. Wait Stats<br />
3. SQL Server IO statistics function (fn_virtualfilestats)<br />
4. System Monitor (Perfmon)<br />
5. Tempdb</p>
<p>Kinda ambitious, I know, but I don&#8217;t blog often and it does beat writing a ten line post on e.g. backup (yawn) compression!</p>
<p>There are plenty of excellent articles which cover IO in some depth, so I&#8217;m going to try my best to avoid regurgitation and try keep things DBA-centric.  (I highly recommend reading the links at the end of the relevant posts to anyone wanting a deeper understanding of IO issues, from configuration to monitoring and troubleshooting).</p>
<p>Until solid state disk prices come down and become ubiquitous, disk access is still a painfully slow (relatively speaking) mechanical process. An IO (read or write) request is issued by SQL Server which is passed onto the OS. The OS then passes this onto your disk controller or HBA and eventually this data request translates into a mechanical arm sitting above a spinning disk which will read or write the data that SQL Server requested.</p>
<p>What this means in performance terms is that disk access will be measured in milliseconds.</p>
<p>Access times to the other major components of a DBMS that directly affect performance such as CPU and RAM can be measured in microseconds or nanoseconds.</p>
<p>As you can see, it&#8217;s an order of magnitude difference.</p>
<p>This single factor alone highlights why reviewing a SQL Server database installation&#8217;s disk configuration can be such a valuable and rewarding exercise.  In fact, I begin many of my SQL Server troubleshooting exercises by eliminating IO performance first.</p>
<p>Prior to SQL Server 2000 SP4 it was difficult to identify IO performance issues without a lot of digging around. The biggest clue SQL Server would give would be the occasional mysterious 17883 (or even 17884) errors, and these were not always guaranteed to be IO related, although IO was usually heavily implicated. SP4 for SQL Server 2000 introduced those &#8217;slow IO&#8217; messages which most DBAs will be familiar with and was a step in the right direction.</p>
<p>SQL Server 2005 introduced various DMVs which would give much greater insight into IO performance, and SQL Server 2008 built on this with the Performance DataWarehouse.  These posts are going to cover methods that can be applied from SQL Server 2000 onwards, so will not delve deeply into features and properties only available in later versions, but focus instead on functions and features available across the last three versions of SQL Server as I know there&#8217;s still a lot of SQL Server 2000 installations out there.</p>
<p><script type="text/javascript"><!--
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
// --></script><br />
<script type="text/javascript"><!--
var pageTracker = _gat._getTracker("UA-5764551-1");
pageTracker._trackPageview();
// --></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eraofdata.com/blog/2010/02/sql-server-and-disk-io/feed/</wfw:commentRss>
		</item>
		<item>
		<title>The SQL Server default trace</title>
		<link>http://www.eraofdata.com/blog/2009/09/the-sql-server-default-trace/</link>
		<comments>http://www.eraofdata.com/blog/2009/09/the-sql-server-default-trace/#comments</comments>
		<pubDate>Fri, 25 Sep 2009 23:15:42 +0000</pubDate>
		<dc:creator>Ajmer Dhariwal</dc:creator>
		
		<category><![CDATA[SQL Server]]></category>

		<category><![CDATA[Default Trace]]></category>

		<category><![CDATA[Performance]]></category>

		<category><![CDATA[SQL Profiler]]></category>

		<category><![CDATA[SQL Trace]]></category>

		<guid isPermaLink="false">http://www.eraofdata.com/blog/?p=161</guid>
		<description><![CDATA[The default SQL Server trace in SQL Server 2005 onwards is a background trace that runs continuously and records event information that, despite the adverse comments on the web about its value, can be useful in troubleshooting problems.

This blog post intends to show that, in the right hands, the default SQL Server trace in SQL [...]]]></description>
			<content:encoded><![CDATA[<p>The default SQL Server trace in SQL Server 2005 onwards is a background trace that runs continuously and records event information that, despite the adverse comments on the web about its value, can be useful in troubleshooting problems.
</p>
<p>This blog post intends to show that, in the right hands, the default SQL Server trace in SQL Server 2005 and SQL Server 2008 can yield very useful information.
</p>
<h3>What is the default trace?</h3>
<p>A default installation of SQL Server 2005/2008 results in a lightweight server-side trace running continuously in the background.<br />
The trace records a handful of events to a trace file which can be loaded up and reviewed in SQL Profiler, just the same as any other trace file.<br />
By default, five trace files are kept in the same folder as the SQL Server error log. Each file has a size limit of 20MB before it rolls over to the next file. After the fifth file is filled, the default trace rolls over to the first file, and so on.<br />
To confirm if the trace is enabled, run the following query:<br />
<code><br />
sp_configure 'default trace enabled'<br />
</code></p>
<p>A <code>run_value</code> of 1 indicates the default trace is enabled and should be active.</p>
<p>If the trace is enabled, the following query will list the details:<br/><br />
<code><br />
select * from sys.traces where is_default = 1<br />
</code></p>
<p>
Amongst the output will be confirmation that the trace is running (<code>is_shutdown</code> will be set to 0 if it is running, otherwise it will be 1). The <code>path</code> column will show the full path and name of the current default trace output file.
</p>
<h3>Interrogating the default trace</h3>
<p>Getting information out of the default trace file is much the same as getting information out of any other SQL Profiler trace file. There are multiple options, from simply loading the file in SQL Profiler, interrogating it directly via T-SQL statements, or by loading it into a table. </p>
<p>I&#8217;ve used all three methods in the past. The method you use depends on your own preferences and immediate requirements.</p>
<p>The examples in this blog will interrogate the trace files directly, as that&#8217;s the quickest way to get up and running and view the contents of the trace.<br />
With more experience, the trace file can be regularly polled via e.g. a stored procedure that can be incorporated into standard health-checks to automatically retrieve events of any interest; you decide what those interesting events are. More on that later.</p>
<p>The queries I will demonstrate in this post reference a system table called <font color="green" face="courier">sys.trace_events</font>. This is used to get the graphical name of the events I&#8217;m interested in (otherwise we will only see the numeric event id, as that is what the default trace records).</p>
<p>This table, introduced in SQL Server 2005 holds details of the all the eventclass ids a SQL trace uses, so you can use this to identify which eventclass id corresponds to which event, which is how I established eventclass 20 is a login failed event (the actual event is Audit login failed).</p>
<h4>First things first</h4>
<p>The first thing to do is isolate the trace file in question.<br />
Assuming the default trace is running and it has a traceid of 1 then the following query will return the name of the trace file. The <font face="Courier New">property</font> column value of 2 relates to the value which holds the name of the output trace file.</p>
<p><code><br />
select value from ::fn_trace_getinfo(1) where property=2<br />
</code></p>
<p>This will tell you the name and location of the current trace file. If this does not overlap with the time of the error you are trying to track down, go to the location of that trace file and identify which of the preceding trace files is from the correct time period. As a precautionary next step, make a copy of the trace file, so you have a permanent record, and also to prevent the file being overwritten during a subsequent rollover.</p>
<h4>Example 1: Finding out who filled up tempdb (or any other database for that matter&#8230;)</h4>
<p> So, let&#8217;s get started. A common scenario for a dba is getting called out overnight because somebody, or some thing has maxed out all the space in e.g. tempdb.<br />
Using the <code>fn_trace_gettable</code> function to interrogate the trace file directly we can get some useful information about the cause. Assume there was an 1105 error just before 21:00 last night (&#8221;Could not allocate space for object &#8216;<temporary system object: nnn>&#8216; in database &#8216;tempdb&#8217;&#8221;).
</p>
<p>
To find this out it&#8217;s just a question of confirming when the exact time the database full error occurred and filtering the trace from that period for events in that database. Deceptively simple isn&#8217;t it? The query below does just that.
</p>
<p><code><br />
select te.name as [event],e.textdata, e.applicationname, convert (varchar(20), object_name(e.objectid)) as object, e.spid, e.duration/1000 as [duration (ms)], e.starttime, e.endtime, e.databasename, e.filename, e.loginname, e.hostname, e.clientprocessid<br />
from fn_trace_gettable(&#8217;C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\LOG\log_326.trc&#8217;, default) e<br />
inner join sys.trace_events te on e.eventclass=te.trace_event_id<br />
where databasename = &#8216;tempdb&#8217;<br />
and e.starttime between &#8216;2009-09-24 20:30&#8242; and &#8216;2009-09-24 21:00&#8242;<br />
order by e.starttime<br />
</code></p>
<h5>Query 1: Who maxed out tempdb (or any other database)?</h5>
<p>
The query tracks all events in tempdb that occurred in tempdb for about half an hour before the tempdb full error.
</p>
<p><img src="http://www.eraofdata.com/blog/data/pics/200909_1.jpg" alt="Fig. 1: Tempdb events" width=546 height=100 align="bottom" /></p>
<h5> Fig. 1: Events leading to tempdb full</h5>
<p>Most of the events causing this type of issue will be object creation and autogrow as can be seen above. What we will also have is what was causing them; <i> applicationname , loginname, spid, hostname and clientprocessid </i>. These should all be enough to identify who or what was responsible for the tempdb issue (see fig. 1).</p>
<p>Sometimes, the objectname column will have the names of the tables being created, and the spid column will allow you to narrow down the trace even further in case tracing events half an hour before the database full problem occurred presents too much or too little data.</p>
<p>Needless to say, but I&#8217;ll say it anyway, change the databasename column to the name of the database you&#8217;re interested if you&#8217;re looking for events leading to the same issue in another database.<br />
Also, the trace can be filtered on object creation (trace_event_id = 46) or data or log file autogrow (trace_event_id = 92 or 93) events
</p>
<p>
Without this information you have nothing to go on, unless you happened to be logged in at the server at the time the issue was occurring and realised that there was a long running transaction running against tempdb which was going to cause it to fill up.<br />
You might also notice that for the autogrow events the Duration column records how long the autogrow took - very useful in identifying potential IO performance isues (or impractical file growth settings).<br />
The <i>object</i> column returns the name of the object being referenced/created. If this query is not run against the trace file on the server that the error occurred on, the chances are this column will contain NULLs, so you can select just the raw objectid instead to view the values and then manually retrieve the object names from the affected server.<br />
Incidentally, the <code>fn_trace_gettable</code> function is also the function used to load a trace file into a database table.
</p>
<h4>Example 2: Isolating login failures</h4>
<p>
OK, so the default trace has some use, but what else can it do?
</p>
<p>Suppose you get the occasional login failure on a system, you&#8217;re not too worried as that&#8217;s inevitable sometimes, but from time to time the login failure is for sa and the ip address logged in the error log doesn&#8217;t correspond to any workstation belonging to a dba who should have access. You should be worried now.
</p>
<p><code><br />
select e.name as eventclass, t.textdata, t.hostname, t.ntusername, t.ntdomainname, t.clientprocessid, t.applicationname, t.loginname, t.spid, t.starttime, t.error from fn_trace_gettable('c:\program files\microsoft sql server\mssql.1\mssql\log\log_329.trc', default) t<br />
inner join sys.trace_events e on t.eventclass = e.trace_event_id where eventclass=20<br />
</code></p>
<h5>Query 2: Isolating where a login failure came from</h5>
<p><img src="http://www.eraofdata.com/blog/data/pics/200909_2.jpg" alt="Fig. 2: Login failures" width=546 height=64 align="bottom" /></p>
<h5>Fig 2: Isolating login failures</h5>
<p>
The query references the trace file from the period of the login failure and retrieves the columns containing all the information that should be needed to pin down where any login failure recorded in that file came from. I&#8217;ve already covered how to use this information in another blog posting covering <a href="http://www.eraofdata.com/blog/2009/01/loginfailures/" target="_new">how to identify the source of login failures</a>.
</p>
<h4>Example 3: Identifying performance issues</h4>
<p>
So we&#8217;ve seen some practical applications like pinning down login failures and database or log growth issues which makes the default trace pay for itself in some ways. But wait, there&#8217;s more.
</p>
<p>The bane of most dbas lives&#8217; are application teams who will describe a performance problem they have: great, you say, what&#8217;s the application and SQL Server database, I&#8217;ll log on and take a look? Oh, it happened last week, some time in the afternoon the application team will reply. That&#8217;s OK, you (want to) say, I&#8217;ll just step into my time machine and go back a few days, or get in touch with my sixth-sense and do the same and carry out some sort of mind-meld with SQL Server and try and find out why it was mis-behaving a few days ago. <font size=1>[Disclaimer] Before you go any further I would like to make clear that none of this will be possible via the default trace. It can do quite a few things, but time-travel and mind-melds are slightly out of scope.</font></p>
<p>
The default trace does offer some hope here. Because by default 5 trace files are kept (including the current trace files) there is a chance that the older trace files may cover the time period in question.
</p>
<p>
The default trace will capture some common performance affecting events, namely:</p>
<ul>
<li> Missing Column Statistics</li>
<li> Hash Warning</li>
<li> Sort Warnings</li>
<li> Missing Join Predicate</li>
</ul>
<p>So, if you still have the trace file from the period in question, a query like the following will pick out all the those events:
</p>
<p><code><br />
select v.name as [Event Class], TextData, SPID, Duration, StartTime, EndTime, DatabaseName, ObjectName, ClientProcessID,<br />
ApplicationName, LoginName, SessionLoginName, NTUserName<br />
from fn_trace_gettable(&#8217;C:\log_180.trc&#8217;, DEFAULT) trc<br />
inner join sys.trace_events v on<br />
trc.EventClass = v.trace_event_id<br />
where<br />
v.name in (&#8217;Missing Column Statistics&#8217;,'Hash Warning&#8217;,'Sort Warnings&#8217;,'Missing Join Predicate&#8217;)<br />
and StartTime between &#8216;2009-09-05 14:00&#8242; and &#8216;2009-09-06 16:00&#8242;<br />
</code></p>
<h5>Query 3: Isolating who made table DDL changes</h5>
<h4>Example 4: Finding performance problem events</h4>
<p>
Let&#8217;s start by clarifiying that this is not going to tell us what the query was that generated these warnings. At the end of the day, the default trace is designed to be a low-impact trace that will not cause any performance or load issues so what it gathers is therefore restricted.<br />
What we do have, however are details of when all these errors occcured, the PID of the offending client process, together with the hostname, application name and login name.
</p>
<p>
Armed with that information, we can then go back into that trace and zoom in on a particular spid or application. By narrowing down the spid or application, but expanding the events we&#8217;re looking at we can find out a bit more about the problem. I use a combination of the time period, spid and clientprocessid to narrow down (see my <a href="http://www.eraofdata.com/blog/2009/01/loginfailures/" target="_new">January 2009 blog</a> on login failures on how to use this handy column to track down exactly what application is connecting).
</p>
<p><code><br />
select v.name as [event class], textdata, spid, duration, starttime, endtime, databasename, objectname, clientprocessid,<br />
hostname, applicationname, loginname, sessionloginname, ntusername<br />
from fn_trace_gettable(&#8217;c:\log_180.trc&#8217;, default) trc<br />
inner join sys.trace_events v on<br />
trc.eventclass = v.trace_event_id<br />
where<br />
starttime between &#8216;2009-09-05 14:00&#8242; and &#8216;2009-09-06 16:00&#8242;<br />
and spid = 57 and clientprocessid = 2044<br />
</code></p>
<h5>Query  4: Narrowing down performance problems</h5>
<p><img src="http://www.eraofdata.com/blog/data/pics/200909_3.jpg" alt="Fig. 3: Performance problem events" width=546 height=170 align="bottom" /></p>
<h5>Fig 3: Performance problem events</h5>
<p>
I&#8217;ve replaced figure 3 with the output of an identical query (but using different time range parameters) trace from a server where the output allowed us to identify what query in a large batch job that was running overnight needed tuning. The output showed what time the query ran, the relevant details of the client application and the names of some of the tables/indexes that were affected .
</p>
<h4>Example 5: Finding out who made changes</h4>
<p>
Perhaps one of the most useful aspects of the default trace is the fact that it records changes made to objects and server configuration changes
</p>
<p>
Suppose someone added or removed a column from a table? The following query highlights who did it:</p>
<p><code><br />
select e.name as eventclass, t.textdata, t.objectid, t.objectname, t.databasename, t.hostname, t.ntusername, t.ntdomainname, t.clientprocessid, t.applicationname, t.loginname, t.spid, t.starttime, t.error from fn_trace_gettable('C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\LOG\log_339.trc', default) t<br />
inner join sys.trace_events e on t.eventclass = e.trace_event_id<br />
where eventclass=164<br />
</code></p>
<h5>Query 5: Isolating who made table DDL changes</h5>
<p>Eventclass 164 is the <en>Object:Altered</en> event, and the above query should show what application and login made the changes.</p>
<p>If the change concerned was e.g. a server configuration change like e.g. the <code>max server memory</code> setting, the following code will isolate the culprit:</p>
<p><code><br />
select e.name as eventclass, t.textdata, t.hostname, t.ntusername, t.ntdomainname, t.clientprocessid, t.applicationname, t.loginname, t.spid, t.starttime, t.error from fn_trace_gettable('C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\LOG\log_339.trc', default) t<br />
inner join sys.trace_events e on t.eventclass = e.trace_event_id where eventclass=22<br />
</code></p>
<h5>Query 5a: Isolating who made server configuration changes</h5>
<p>
Eventclass 22 is the <en>ErrorLog</en> event, so of course the changes can also be seen in the error log, but the error log won&#8217;t give you the details of who made the change.
</p>
<h3>Disabling the default trace</h3>
<p>
The chances are, most SQL Server 2005 (and beyond) systems are running this trace in the background without anyone even being aware of them, which in itself highlights what low impact these traces have. However, if CPU or disk resources are scarce the default trace can be disabled.<br />
To disable the default trace, run the following commands:
</p>
<p><code><br />
sp_configure 'default trace', 0<br />
go<br />
reconfigure with override<br />
go<br />
</code></p>
<h3>And finally&#8230;</h3>
<p>
This post was not intended to show ever possible piece of information that can be extracted from the default trace, but merely to highlight some of the things that can be found without too much effort.<br />
Have a look at your own trace files as everyone installation and every application running against SQL Server is different - it&#8217;s a great way of pro-actively finding potential issues. </p>
<p>
So go and explore&#8230;
</p>
<p><script type="text/javascript"><!--
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
// --></script><br />
<script type="text/javascript"><!--
var pageTracker = _gat._getTracker("UA-5764551-1");
pageTracker._trackPageview();
// --></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eraofdata.com/blog/2009/09/the-sql-server-default-trace/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Upgrading/Changing SQL Server editions</title>
		<link>http://www.eraofdata.com/blog/2009/05/upgradingchanging-sql-server-editions/</link>
		<comments>http://www.eraofdata.com/blog/2009/05/upgradingchanging-sql-server-editions/#comments</comments>
		<pubDate>Tue, 26 May 2009 06:13:42 +0000</pubDate>
		<dc:creator>Ajmer Dhariwal</dc:creator>
		
		<category><![CDATA[SQL Server]]></category>

		<category><![CDATA[Installation]]></category>

		<category><![CDATA[Setup]]></category>

		<category><![CDATA[SQL Server Edition]]></category>

		<category><![CDATA[Upgrading]]></category>

		<guid isPermaLink="false">http://www.eraofdata.com/blog/?p=156</guid>
		<description><![CDATA[With the updated SQL Server 2008 installation program, upgrading SQL Server editions has finally been made as straightforward process as it always should have been.
In SQL Server 2005 upgrading/changing the edition involved running the setup program from the command line with a multitude of specific parameters set.
This blog posting covers SQL Server 2005 and SQL [...]]]></description>
			<content:encoded><![CDATA[<p>With the updated SQL Server 2008 installation program, upgrading SQL Server editions has finally been made as straightforward process as it always should have been.<br />
In SQL Server 2005 upgrading/changing the edition involved running the setup program from the command line with a multitude of specific parameters set.<br />
This blog posting covers SQL Server 2005 and SQL Server 2008.<br />
Before you start, make sure you are on a supported upgrade path as described in the <a href="http://msdn.microsoft.com/en-us/library/ms143393(SQL.90).aspx">edition upgrade matrix</a> (for SQL Server 2005) on Microsoft&#8217;s website.<br />
Assuming you are on a supported upgrade path (i.e. not trying to &#8216;upgrade&#8217; a 32-bit edition of SQL Server to a 64-bit edition), the next step is to dig out the installation files and run the setup program.</p>
<h4>Upgrading SQL Server 2005</h4>
<p>This is where it gets unnecessarily complicated if you&#8217;re upgrading a SQL Server 2005 installation.<br />
Basically, you have to run the install via the command line and send the setup program the relevant parameters to upgrade the relevant SQL Server components you want to upgrade.<br />
So, assuming you want to upgrade SQL Server 2005 Standard Edition to SQL Server 2005 Enterprise Edition, you&#8217;d <a href="http://support.microsoft.com/kb/126410">open a DOS prompt</a> in the same folder as the install media and run the following command:<br />
<code><br />
start /wait setup.exe ADDLOCAL=SQL_Engine INSTANCENAME=Desktop\SQL2K5EXP UPGRADE=SQL_Engine SKUUPGRADE=1 /qb<br />
</code><br />
The above command line upgrades the SQL Server instance named SQL2K5EXP on machine Desktop to whatever edition the install media contains.</p>
<p>The multitude of parameters here is what causes all the confusion and the problems when trying to carry out something that should really be fairly straightforward, so make sure your syntax matches what is above when you&#8217;re trying the upgrade.</p>
<p>A compromise method involves invoking the gui component of the installation by running just the following:<br />
<code><br />
start /wait setup.exe SKUUPGRADE=1<br />
</code><br />
Which will start the graphical setup program and step through the appropriate options to complete the edition upgrade.</p>
<h4>Upgrading SQL Server 2008</h4>
<p>Fortunately, Microsoft have realised the upgrade mechanism offered for SQL Server 2005 was lacking in some areas and have addressed the issue via the gui component of the setup program, so now the same setup program you use to install SQL Server 2008 can be used to upgrade SQL Server 2008. Crazy idea, I know.</p>
<p>
Figure 1 below shows the setup GUI with the <b>Installation Maintenance</b> option selected:
</p>
<p><img src="http://www.eraofdata.com/blog/data/pics/ed_upgr1.jpg" alt="Fig. 1: Installation Maintenance" width=546 height=398 align="bottom" />
</p>
<p>
Clicking on <en>Edition Upgrade</en> will result in a couple of <en>Setup Support Rules</en> checks running which will check e.g. whether the WMI service is running, the OS version and whether the user running the setup application has the relevant privileges to complete the install.
</p>
<p>Assuming you get past these checks you&#8217;ll end up at Figure 2 which is the <en>Product Key</en> dialog where you can select either a free edition to &#8216;upgrade&#8217; to or enter the Product Key that accompanies the edition of SQL Server that the setup program was launched from.
</p>
<p>
Figure 2 below shows the setup GUI with the <b>Product Key</b> option selected:
</p>
<p>
<img src="http://www.eraofdata.com/blog/data/pics/ed_upgr2.jpg" alt="Fig. 2: Product Key" width=533 height=400 align="bottom" />
</p>
<p>
After selecting the relevant option the install is pretty much the same as a normal install; accept the licence agreement, choose the instance name to be upgraded and let the install run through and complete. Simple.
</p>
<h4>Validating the edition change</h4>
<p>The following query will confirm the edition of SQL Server:<br />
<code><br />
select serverproperty('Edition')<br />
</code><br />
This will return the edition, and if the install went through correctly it will confirm that SQL Server is now running the expected edition.</p>
<h4>Troubleshooting the installation</h4>
<p>A run-through of troubleshooting SQL Server installs is slightly out of scope and there are plenty of KBs and webcasts on the Microsoft web site that will take you through that.<br />
All I will say is look at the summary.txt file in the setup bootstrap directory (default location is C:\Program Files\Microsoft SQL Server\100\Setup Bootstrap\Log) to identify the initial failure. This should provide you with a summary of the problem and further details will be found in a subdirectory off the location of the summary.txt file which will be named after the date and time of the setup run.</p>
<h4>Useful links</h4>
<p><a target="_blank" href="http://msdn.microsoft.com/en-us/library/ms144259(SQL.90).aspx#skuupgrade">How to: Install SQL Server 2005 from the Command Prompt</a><br />
<a target="_blank" href="http://msdn.microsoft.com/en-us/library/cc707783.aspx">How to: Upgrade to a Different Edition of SQL Server 2008 (Setup)</a></p>
<p><script type="text/javascript"><!--
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
// --></script><br />
<script type="text/javascript"><!--
var pageTracker = _gat._getTracker("UA-5764551-1");
pageTracker._trackPageview();
// --></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eraofdata.com/blog/2009/05/upgradingchanging-sql-server-editions/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Verifying and troubleshooting SQL Server listener connectivity</title>
		<link>http://www.eraofdata.com/blog/2009/03/verifying-and-troubleshooting-sql-server-listener-connectivity/</link>
		<comments>http://www.eraofdata.com/blog/2009/03/verifying-and-troubleshooting-sql-server-listener-connectivity/#comments</comments>
		<pubDate>Tue, 03 Mar 2009 21:36:47 +0000</pubDate>
		<dc:creator>Ajmer Dhariwal</dc:creator>
		
		<category><![CDATA[SQL Server]]></category>

		<category><![CDATA[connectivity]]></category>

		<category><![CDATA[listener]]></category>

		<category><![CDATA[Named Pipes]]></category>

		<category><![CDATA[Shared Memory]]></category>

		<category><![CDATA[TCP/IP]]></category>

		<guid isPermaLink="false">http://www.eraofdata.com/blog/?p=132</guid>
		<description><![CDATA[SQL Server can listen for connection requests via multiple listeners, such as TCP/IP, shared memory or named pipes.
Sometimes it&#8217;s necessary to check connectivity by a specific listener.
The first place to check is the error log to confirm that the relevant listener has started up; the error log is the SQL Server version independent way of [...]]]></description>
			<content:encoded><![CDATA[<p>SQL Server can listen for connection requests via multiple listeners, such as TCP/IP, shared memory or named pipes.<br />
Sometimes it&#8217;s necessary to check connectivity by a specific listener.<br />
The first place to check is the error log to confirm that the relevant listener has started up; the error log is the SQL Server version independent way of confirming listener startup instead of relying on e.g. the <code>sys.endpoints</code> catalog view available from SQL Server 2005 onward.<br />
The output below is from a SQL Server 2005 server with TCP/IP, shared memory, named pipes and the dedicated administrator connection listeners enabled.</p>
<div><code><br />
2009-02-24 08:15:40.050 Server Server local connection provider is ready to accept connection on [ \\.\pipe\SQLLocal\SQL2K5DEV ].<br />
2009-02-24 08:15:40.050 Server Server named pipe provider is ready to accept connection on [ \\.\pipe\MSSQL$SQL2K5DEV\sql\query ].<br />
2009-02-24 08:15:40.050 Server Server is listening on [ 'any' 49839].<br />
2009-02-24 08:15:40.050 Server Dedicated admin connection support was established for listening remotely on port 49839.</code></div>
<p>
So, the relevant listener is in the list and, as far as SQL Server is concerned, is up and running.The next step is to check connectivity. The SQL Server clients are best for this, as this will take the problem client application out of the equation (presumably you&#8217;re reading this blog because of issues connecting to SQL Server via a specific listener).<br />
Either SQL Server Management Studio (SSMS), or the lightweight console mode SQLCMD, which is my preferred tool will do here.<br />
To test each listener you just need to prefix the listener name abbreviation before the server name. Using SQLCMD as an example and a local instance by the name of .\SQL2K5DEV:</p>
<table border="1">
<tbody>
<tr>
<td><strong>Listener</strong></td>
<td><strong>Prefix</strong></td>
<td><strong>Example Server Name entry in SSMS</strong></td>
</tr>
<tr>
<td>Named Pipes</td>
<td>np</td>
<td>np:\\.\pipe\MSSQL$SQL2K5DEV\sql\query</td>
</tr>
<tr>
<td>Shared Memory</td>
<td>lpc</td>
<td>lpc:.\SQL2K5DEV</td>
</tr>
<tr>
<td>TCP/IP</td>
<td>tcp</td>
<td>tcp:.\SQL2K5DEV,50334<br />
<sup>Note the use of the TCP/IP port number</sup></td>
</tr>
<tr>
<td>VIA Protocol</td>
<td>via</td>
<td>via:.\SQL2K5DEV,1433,0<br />
<sup>Note the use of port number <em>and</em> the network card number (0 if it&#8217;s the only card)</sup></td>
</tr>
</tbody>
</table>
<p>In case you didn&#8217;t know the &#8216;.&#8217; where the host name should be prior to the instance name is a shortcut for the local machine name.<br />
If you prefer to use SQLCMD, just put the listener prefix in front of the server name as listed in the prefix column after the -S (server name) parameter.</p>
<p>To verify what protocol a connected client is using, use the following query from SQL Server 2005 onwards:</p>
<p>select net_transport, endpoint_id, connect_time,client_net_address from sys.dm_exec_connections where session_id = @@spid;Replace @@spid with the spid (or session_id) of the connection you&#8217;re interested in.</p>
<p>This will output the listener the relevant connection is using as well as information like the endpoint id (which can be checked against <code>sys.endpoints</code>), when the connection came in and its address, as shown below (I dispensed with the <code>column</code> to make the output easier to read):</p>
<div><code><br />
net_transport endpoint_id connect_time<br />
------------- ----------- ----------------------<br />
Named pipe              3 2009-03-03 08:59:07.31<br />
</code></div>
<p>
If you can confirm the listener is up and you can connect to it (at least locally) you know it&#8217;s not a SQL Server configuration issue and is more likely to be a firewall/network problem, so make sure the relevant ports you&#8217;re listening on are open, e.g. named pipes required port 445 to be open.
</p>
<p>
Just to prove it, go to the client machine(s) concerned and verify if the <a href="http://support.microsoft.com/kb/310099">Microsoft port querying tool</a> can connect to that server. Fortunately, there is a <a href="http://www.microsoft.com/Downloads/details.aspx?familyid=8355E537-1EA6-4569-AABB-F248F4BD91D0&amp;displaylang=en">graphical interface</a> for this tool. This is a very useful tool for identifying, or at least narrowing down, connectivity problems.
</p>
<p>
Happy troubleshooting!
</p>
<p><script type="text/javascript"><!--
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
// --></script><br />
<script type="text/javascript"><!--
var pageTracker = _gat._getTracker("UA-5764551-1");
pageTracker._trackPageview();
// --></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eraofdata.com/blog/2009/03/verifying-and-troubleshooting-sql-server-listener-connectivity/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Identifying the source of SQL Server login failures (18456 errors)</title>
		<link>http://www.eraofdata.com/blog/2009/01/loginfailures/</link>
		<comments>http://www.eraofdata.com/blog/2009/01/loginfailures/#comments</comments>
		<pubDate>Fri, 23 Jan 2009 13:07:18 +0000</pubDate>
		<dc:creator>Ajmer Dhariwal</dc:creator>
		
		<category><![CDATA[SQL Server]]></category>

		<category><![CDATA[18456]]></category>

		<category><![CDATA[login failures]]></category>

		<category><![CDATA[SQL Profiler]]></category>

		<guid isPermaLink="false">http://www.eraofdata.com/blog/?p=114</guid>
		<description><![CDATA[We&#8217;ve all had to isolate the source of login failures from time to time, and after seeing a bit of a surge in the number of queries on the forums asking for help with just this problem, I thought I&#8217;d start the new year with a quick way of pinning down these failures; I did [...]]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve all had to isolate the source of login failures from time to time, and after seeing a bit of a surge in the number of queries on the forums asking for help with just this problem, I thought I&#8217;d start the new year with a quick way of pinning down these failures; I did hunt around on the web assuming this must have been a topic that had been locked down plenty of times on other sites/blogs, but I was rather surprised that, whilst there are plenty of articles on what a login failure is and what all the codes returned in the error message mean etc, I could not find anything that went through all the steps a dba would need to go through to narrow down exactly where the failed login request was coming from.<br />
This blog is my attempt to plug that gap and show you how to isolate the exact process causing the problem.</p>
<p>The technique is pretty much version independent, so is not specific to any particular version, but I will assume you know how to use <em>SQL Server Profiler</em> and capture a trace.</p>
<h3>Login Error 18456</h3>
<p>A login failure will throw an 18456 error and will be accompanied by the following entry in the SQL Server error log (SQL Server 2000 tends not to display the IP address):<br />
<code><br />
2009-01-15 09:40:24.55 Logon       Error: 18456, Severity: 14, State: 8.<br />
2009-01-15 09:40:24.55 Logon       Login failed for user 'Domain\User'. [CLIENT: xxx.xxx.xxx.xxx]<br />
</code></p>
<p>The <a href="http://msdn.microsoft.com/en-us/library/ms164086(SQL.90).aspx">severity</a> of the error indicates the seriousness of the error. A severity level of 14 indicates an error in the range described as user correctable, which is understandable for login failures.</p>
<p>The next item of information the error provides is the state number. Most errors have a state number associated with them which provides further information which is usually unique to the error that has been thrown. For a login error, state: 8, shown in the above example, indicates an invalid password was used.</p>
<p>The state number therefore provides invaluable information about the reason for the login failure and can often be enough to identify the cause of an 18456 error.</p>
<p>The table below illustrates what some of these state values mean:</p>
<table border="1">
<tbody>
<tr>
<td>State</td>
<td>Error description</td>
</tr>
<tr>
<td>1</td>
<td>Account is locked out</td>
</tr>
<tr>
<td>2</td>
<td>User id is not valid</td>
</tr>
<tr>
<td>5</td>
<td>User id is not valid</td>
</tr>
<tr>
<td>7</td>
<td>The login being used is disabled</td>
</tr>
<tr>
<td>8</td>
<td>Incorrect password</td>
</tr>
<tr>
<td>9</td>
<td>Invalid password</td>
</tr>
<tr>
<td>11-12</td>
<td>Login valid but server access failed</td>
</tr>
<tr>
<td>16</td>
<td>Login valid, but not permissioned to use the target database</td>
</tr>
<tr>
<td>18</td>
<td>Password expired</td>
</tr>
<tr>
<td>27</td>
<td>Initial database could not be found</td>
</tr>
<tr>
<td>38</td>
<td>Login valid but database unavailable (or login not permissioned)</td>
</tr>
</tbody>
</table>
<p>The next item of information is the login (SQL Server or Windows) generating the failure, followed by the IP address of the host from which the login was attempted,  which provides a useful cross-reference to confirm we&#8217;re looking at the right host when we&#8217;re trying to isolate the login failure.</p>
<h3>Isolating the login failure</h3>
<p>If the information provided in the error log for the login failure is not enough to isolate the source of the errors, the next step is to run a quick trace against SQL Server to get more information.<br />
The easiest way to identify the process generating the login failures is via a <em>SQL Server Profiler</em> (SSP) trace.
</p>
<p>
If you&#8217;re running SQL Server 2005 or above and you still have the default trace enabled (which is on by default in an out-of-the-box installation) then you don&#8217;t need to start a new trace; check out my <a href="http://www.eraofdata.com/blog/2009/09/the-sql-server-default-trace" target="_new">SQL Server default trace</a> post instead.
</p>
<p>
If the default trace is disabled, or you have an earlier version of SQL Server, then read on.
</p>
<p>Start SSP, and, using either your favourite trace template, or via a new trace template (<em>File</em> &gt; <em>Templates</em> &gt; <em>New Templates&#8230;</em>) make sure the following columns are selected:</p>
<ul>
<li><em>ClientProcessID</em></li>
<li><em>Hostname</em></li>
<li><em>LoginName</em></li>
<li><em>NTUserName</em></li>
<li><em>NTDomainName</em></li>
<li><em>ApplicationName</em></li>
</ul>
<p>These columns can be found in the <em>Trace Properties</em> dialog on the <em>Events Selection</em> tab. If they are not visible, tick the <em>Show all columns</em> checkbox. Note that SPID is always a selected column by default, and cannot be de-selected.</p>
<p>Under <em>Events</em> select the <em>Audit Login Failed</em> event under <em>Security Audit</em>. As we&#8217;re only interested in login failures this is the only event we need, and will help ensure that any performance impact from running the trace can be kept to a minimum. On a production system it&#8217;s never really advisable (imho) to run a graphical SSP trace on the server; always use a <a href="http://msdn.microsoft.com/en-us/library/ms191443(SQL.90).aspx">server side trace.</a></p>
<p>Figure 1 below shows a completed trace template:</p>
<p><img src="http://www.eraofdata.com/blog/data/pics/loginErrs_Fig1.jpg" alt="Fig. 1: Completed trace template" width="440" height="279" align="bottom" /></p>
<p>It might look a bit sparse, but we are only interested in a specific error.</p>
<p><strong>Step 1</strong><br />
Save the modified trace template and launch a new trace, specifying the saved template as the template for the new trace and wait for the login failures. Stop the trace after a login failure has been generated.</p>
<p><strong>Step 2</strong><br />
The Hostname column should have recorded the name of the server that the invalid login emanated from and the ClientProcessID should have captured the Process ID, or PID of the offending process (or processes if there are multiple processes involved).</p>
<p><strong>Step 3</strong><br />
Log on to the server triggering the errors, and list the PIDs of the relevant processes. This can be done using <a href="http://technet.microsoft.com/en-us/library/bb491010.aspx">Tasklist</a> or Task Manager.</p>
<p>To view the PIDs via <em>Task Manager</em>, start <em>Task Manager</em> (Shift+Ctrl+Esc), go to <em>View</em> &gt; <em>Select Columns&#8230;</em> and tick the checkbox labelled <em>PID (Process Identifier)</em> and click <em>OK</em>.</p>
<p>Click on the <em>Processes</em> tab to bring all the processes running on that server into view (make sure <em>Show all processes from all users</em> is ticked) and click on the <em>PID</em> column heading to sort the PIDs in descending or ascending order.</p>
<p><strong>Step 4</strong><br />
Once you&#8217;ve isolated the process responsible via the <em>PID</em> it should just be a matter of identifying where that process stores the credentials it uses for logging into SQL Server and verifying them.<br />
Usually, the process will be a service, so it&#8217;s just a question of bringing up the <em>Services</em> plugin via <em>Control Panel</em>, or <em>Start</em> &gt; <em>Run</em> &gt; <em>services.msc</em> should also do the trick.</p>
<p>That&#8217;s it, so happy hunting!</p>
<p><strong>Useful links</strong><br />
<a href="http://msdn.microsoft.com/en-us/library/ms366351(SQL.90).aspx">Troubleshooting: Login failed for user &#8216;x&#8217;</a><br />
<a href="http://blogs.msdn.com/sql_protocols/archive/2006/02/21/536201.aspx">Understanding &#8220;login failed&#8221; (Error 18456) error messages in SQL Server 2005</a></p>
<p><script type="text/javascript"><!--
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
// --></script><br />
<script type="text/javascript"><!--
var pageTracker = _gat._getTracker("UA-5764551-1");
pageTracker._trackPageview();
// --></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eraofdata.com/blog/2009/01/loginfailures/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Orphaned MSDTC transactions (-2 spids)</title>
		<link>http://www.eraofdata.com/blog/2008/12/orphaned-msdtc-transactions-2-spids/</link>
		<comments>http://www.eraofdata.com/blog/2008/12/orphaned-msdtc-transactions-2-spids/#comments</comments>
		<pubDate>Fri, 12 Dec 2008 18:37:59 +0000</pubDate>
		<dc:creator>Ajmer Dhariwal</dc:creator>
		
		<category><![CDATA[SQL Server]]></category>

		<category><![CDATA[-2]]></category>

		<category><![CDATA[distributed transactions]]></category>

		<category><![CDATA[MSDTC]]></category>

		<category><![CDATA[orphaned transcations]]></category>

		<guid isPermaLink="false">http://www.eraofdata.com/blog/?p=99</guid>
		<description><![CDATA[Any dba looking after a server that takes part in a distributed transaction will probably have come across situations where a spid that does not show up in master..sysprocesses or sys.dm_exec_sessions (depending on your version of SQL Server) ends up blocking other users.
Looking at sp_lock or the syslockinfo table or sys.dm_tran_locks would show resources being [...]]]></description>
			<content:encoded><![CDATA[<p>Any dba looking after a server that takes part in a distributed transaction will probably have come across situations where a spid that does not show up in master..sysprocesses or sys.dm_exec_sessions (depending on your version of SQL Server) ends up blocking other users.<br />
Looking at sp_lock or the syslockinfo table or sys.dm_tran_locks would show resources being locked by a spid with a value of -2. Spids with a negative cannot be killed in SQL Server.</p>
<p>Often, especially in older (pre version 7) versions of SQL Server the only option was to restart SQL Server to clear those locks.</p>
<p>Spids with a value of -2 are orphaned distributed transactions.</p>
<h4>What&#8217;s an orphaned distributed transaction?</h4>
<p>In a nutshell, it&#8217;s a distributed transaction for which the transactional state is unknown. A distributed transaction is a database transaction that involves more than one database system (usually) located on different servers.</p>
<p>In order to maintain transactional consistency these transactions need to be coordinated, as we can&#8217;t have one database involved in the transaction committing the data it has been sent, whilst another database fails to commit the data it has been sent; either all the data is successfully committed by all the databases involved in the transaction, or the entire transaction is rolled back and no data gets committed by any database.</p>
<p>The process responsible for coordinating these transactions is the Microsoft Distributed Transaction Coordinator, or MSDTC.</p>
<p>You can get to it via Start &gt; Run and typing <code>dcomcnfg</code>.</p>
<p>This brings up the Component Services plugin. Click on Component Services &gt; Computers &gt; My Computer.</p>
<p>Under My Computer, click on the Distributed Transaction Coordinator folder. Under Local DTC you will find the following information about MSDTC transactions:</p>
<ul>
<li><strong>Transaction List</strong> lists active distributed transactions</li>
<li><strong>Transaction Statistics</strong> reports summary statistics for previous MDTC transactions</li>
</ul>
<p>Both items provide useful information, but in the context of this blog, it&#8217;s the <strong>Transaction List</strong> that we&#8217;re interested in. This lists all the currently active transactions that have not been successfully completed, or been successfully rolled back. The <code>status</code> column lists what MSDTC regards as the state of the transaction, and the Unit of Work ID column with those strange <a href="http://msdn.microsoft.com/en-us/library/aa368767.aspx">GUID</a> type values displays a unique identifier that is assigned to that transaction; this is a very useful value to have as it will allow any MSDTC transactions executing within SQL Server, including transactions that MSDTC has lost control of and thus have a spid value of -2 to be killed by specifying the Unit of Work (UoW) ID in place of the spid number.</p>
<p>If the status of the transaction is &#8216;Active&#8217; then the transaction is currently executing. Figure 1 below, shows an active distributed transaction when viewed in the MSDTC console (the screenshot was taken from a Windows Vista host):</p>
<p align="center"><img src="http://www.eraofdata.com/blog/data/pics/fig1_msdtc.jpg" alt="Figure 1: The MSDTC Console" width="440" align="bottom" /></p>
<p align="right"><span style="font-size: xx-small;"><br />
<code>Figure 1: The MSDTC Console</code><br />
</span></p>
<p>The problems arises when, for whatever reason, MSDTC loses track of one of these transactions. The transaction will then be marked as in-doubt. If this happens, SQL Server will honour the locks the spid bound to that transaction has left, but the spid itself will be assigned a negative value of -2.<br />
This means it will no longer appear as an internal process, but will still cause blocking if the original transaction was accessing tables at the time it went awol, as the locks will have been retained. This is because SQL Server does not know whether it can commit or roll back the transaction as MSDTC has control of distributed transactions.</p>
<p>Sometimes this can happen because the MSDTC service (Distributed Transaction Coordinator) has been terminated, or is in a hung state. When MSDTC is restarted it will go through its own internal transaction log and try and regain control of transactions that were left open in order to commit them or roll them back. Usually, however, it&#8217;s because one or more of the applications involved in the distributed transaction has mis-behaved in some way and has hung or crashed, and the only option there is to try and restart the application(s) concerned and hope that it has a recovery mechanism to deal with any MSDTC transactions that were left unresolved.</p>
<p>From time to time, therefore, MSDTC will not be able to regain control of a transaction and it&#8217;s status will be marked as &#8216;in-doubt&#8217; in MSDTC coordinator.</p>
<p>Once upon a time (well, before SQL Server 6.5 SP5 at any rate) the only way to resolve this was to restart SQL Server, and because this was once the only way to deal with orphaned spids, it&#8217;s become something of a myth that it remains the only way to deal with the issue.</p>
<p>However, since Service Pack 5 of SQL Server 6.5 (http://support.microsoft.com/default.aspx/kb/195542) it has been possible to kill an MSDTC transaction by specifying something called the UoW ID, as opposed to just the spid number.</p>
<h4>The Unit of Work (UoW) ID</h4>
<p>The UoW ID is a 24 character GUID assigned to each transaction issued by MSDTC, and it is the UoW ID that is used to identify and kill orphaned MSDTC transactions in SQL Server.</p>
<h4>Killing an MSDTC transaction</h4>
<p>So, you&#8217;ve come up against an MSDTC transaction that the MSDTC service has been unable to successfully commit or rollback.</p>
<p>This transaction has now left locks on your data that are associated with an orphaned spid and you&#8217;ve got no option but to kill that MSDTC transaction.</p>
<p>You first need to retrieve the UoW ID. This should be displayed in the MSDTC console, but to verify it is the correct ID, you can interrogate the <code>syslockinfo</code> system view. Specifically the req_transactionUoW column. The req_spid column in syslockinfo hosts the spid number.<br />
So, to pull out the UoW ID for an orphaned MSDTC transaction you just need to run the following query:</p>
<p><code><br />
select req_transactionUoW as [UoW ID] from syslockinfo where req_spid = -2<br />
</code></p>
<p>The above will work in SQL Server 2000 onward, but from SQL Server 2005, it&#8217;s more politically correct to use the DMVs, so the table and column names have changed slightly:</p>
<p><code><br />
select request_owner_guid as [UoW ID] from sys.dm_tran_locks where request_session_id = -2<br />
</code><br />
Then to kill it:<br />
<code><br />
kill {'UoW ID'}<br />
</code><br />
where <code>{UoW ID}</code> is the result of the above select statement.</p>
<p>However, this must only be done as an absolute <em>last</em> resort when other methods, such as e.g. restarting the applications/processes involved in that distributed transaction do not resolve the problem. At the end of the day, those locks will have been placed for a reason, so you don&#8217;t know if the consistency of the data is now compromised especially if syslockinfo/sys.dm_tran_locks shows the MSDTC transaction(s) concerned have placed exclusive locks on objects.</p>
<h5>Useful MSDTC links</h5>
<p><a href="http://msdn.microsoft.com/en-us/library/aa561924.aspx">Troubleshooting Problems with MSDTC</a><a><br />
</a><a href="http://msdn.microsoft.com/en-us/library/ms679479(VS.85).aspx">New DTC Functionality in Windows Server 2003 Service Pack 1</a><br />
<a href="http://technet.microsoft.com/en-us/library/cc725913.aspx">Enable Firewall Exceptions for MS DTC</a></p>
<p><script type="text/javascript"><!--
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
// --></script></p>
<p><script type="text/javascript"><!--
var pageTracker = _gat._getTracker("UA-5764551-1");
pageTracker._trackPageview();
// --></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eraofdata.com/blog/2008/12/orphaned-msdtc-transactions-2-spids/feed/</wfw:commentRss>
		</item>
		<item>
		<title>How many CPUs can SQL Server use?</title>
		<link>http://www.eraofdata.com/blog/2008/11/how-many-cpus-can-sql-server-use/</link>
		<comments>http://www.eraofdata.com/blog/2008/11/how-many-cpus-can-sql-server-use/#comments</comments>
		<pubDate>Mon, 03 Nov 2008 21:59:43 +0000</pubDate>
		<dc:creator>Ajmer Dhariwal</dc:creator>
		
		<category><![CDATA[SQL Server]]></category>

		<category><![CDATA[CPUs]]></category>

		<category><![CDATA[Licensing]]></category>

		<category><![CDATA[Multicore]]></category>

		<guid isPermaLink="false">http://www.eraofdata.com/blog/?p=57</guid>
		<description><![CDATA[Simple question, you might think. But judging by how often this question is asked on the SQL Server forums its clear that there is a lot of confusion about exactly how many CPUs a particular edition of SQL Server can use.
The problem is due mainly to incorrect assumptions about Microsoft&#8217;s licencing policy, combined with the [...]]]></description>
			<content:encoded><![CDATA[<p>Simple question, you might think. But judging by how often this question is asked on the SQL Server forums its clear that there is a lot of confusion about exactly how many CPUs a particular edition of SQL Server can use.</p>
<p>The problem is due mainly to incorrect assumptions about Microsoft&#8217;s licencing policy, combined with the prevalence of multi-core CPUs. This has led to some incorrect responses to postings about CPU licencing on the SQL Server forums, so I&#8217;ve decided it&#8217;s worth blogging about to try and clarify the matter.</p>
<p>This blog covers SQL Server 2005 and SQL Server 2008 editions.</p>
<p><a href="http://www.microsoft.com/Sqlserver/2005/en/us/special-considerations.aspx">Microsoft&#8217;s multicore licencing policy</a> is based on the number of sockets on the motherboard, <strong>not</strong> the number of cores. Therefore, for licencing purposes, a dual-core CPU equates to 1 CPU, not 2. A quad-core CPU equates to 1 CPU, not 4 etc. etc.</p>
<p>What this means is that your free SQL Express edition can use all 4 cores in your quad core CPU and not just one core. You can verify this by interrogating the <code>sys.dm_os_sys_info</code> SQL Server dynamic management view (DMV), particularly. The following query lists how many CPUs a particular instance of SQL Server can see:<br />
<code><br />
select cpu_count from sys.dm_os_sys_info<br />
</code><br />
The <code>sys.dm_os_sys_info</code> DMV returns a whole host of other useful information that used to be very difficult to obtain without using xp_cmdshell or WMI, so it&#8217;s well worth exploring further. In fact, most of the DMVs have invaluable information if you know what you&#8217;re looking for, but that&#8217;s a subject area far too large for a blog; just peruse Books Online as that gives a complete listing and a good rundown of what each DMV offers.<br />
Just remember that, although SQL Server Express will be able to see multiple CPUs and use them to improve concurrency, SQL Server Express edition is prevented from taking advantage of this to parallelise individual queries, which basically means that an individual query will not be executed across multiple CPUs.</p>
<p>Back to counting CPUs. To find out how many CPUs a particular instance is actually using, run the following query which is based on the <code>sys.dm_os_schedulers</code> DMV:<br />
<code><br />
select scheduler_id,cpu_id, status, is_online from sys.dm_os_schedulers where status='VISIBLE ONLINE'<br />
</code><br />
This will return how many CPUs SQL Server is using. Ordinarily, this will always equate to the number of CPUs on the system. However, if CPU affinity is being used to assign specific CPUs to SQL Server, this query will show how many CPUs SQL Server is actively using.</p>
<p>Run the query without the <code>where</code> clause to see how many CPUs are offline and not being used (their status will be VISIBLE OFFLINE). The additional rows returned will show up internally used schedulers such as the scheduler retained for the Dedicated Admin Connection, or DAC.</p>
<p>If this query does not list the number of CPUs you are expecting, and you&#8217;re definitely not using CPU affinity, check the edition of SQL Server that you are using against the table below.</p>
<p><strong>Note:</strong> To confirm that CPU affinity is indeed being used, the <code>cpu_id</code> column will always be less than 255.</p>
<p>A summary of how many CPUs the different editions of SQL Server support is summarised in the table below:</p>
<table border="1" width="446">
<tbody>
<tr>
<td>SQL Server version</td>
<td>Express</td>
<td>Workgroup</td>
<td>Web</td>
<td>Standard</td>
<td>Enterprise</td>
</tr>
<tr>
<td>SQL Server 2005</td>
<td>1</td>
<td>2</td>
<td>n/a</td>
<td>4</td>
<td>Unlimited<sup>1</sup></td>
</tr>
<tr>
<td>SQL Server 2008</td>
<td>1</td>
<td>2</td>
<td>4</td>
<td>4</td>
<td>Unlimited<sup>1</sup></td>
</tr>
<tr>
<td colspan="6"><span style="font-size: x-small;"><br />
<sup>1</sup>Subject to OS limits</p>
<p></span></td>
</tr>
</tbody>
</table>
<p><script type="text/javascript"><!--
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
// --></script><br />
<script type="text/javascript"><!--
var pageTracker = _gat._getTracker("UA-5764551-1");
pageTracker._trackPageview();
// --></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eraofdata.com/blog/2008/11/how-many-cpus-can-sql-server-use/feed/</wfw:commentRss>
		</item>
		<item>
		<title>SQL Server memory configuration</title>
		<link>http://www.eraofdata.com/blog/2008/10/sql-server-memory-configuration/</link>
		<comments>http://www.eraofdata.com/blog/2008/10/sql-server-memory-configuration/#comments</comments>
		<pubDate>Sun, 19 Oct 2008 19:59:18 +0000</pubDate>
		<dc:creator>Ajmer Dhariwal</dc:creator>
		
		<category><![CDATA[SQL Server]]></category>

		<category><![CDATA[/3gb]]></category>

		<category><![CDATA[/pae]]></category>

		<category><![CDATA[awe enabled]]></category>

		<category><![CDATA[buffer cache]]></category>

		<category><![CDATA[configuring memory]]></category>

		<category><![CDATA[memtoleave]]></category>

		<category><![CDATA[procedure cache]]></category>

		<category><![CDATA[SQL Server Memory Management]]></category>

		<category><![CDATA[VAS]]></category>

		<category><![CDATA[virtual address space]]></category>

		<guid isPermaLink="false">http://www.eraofdata.com/blog/?p=3</guid>
		<description><![CDATA[One of the things that I frequently come across when reviewing SQL Server installations is just how many of them have not been set up with appropriate memory configuration settings, or, as in many cases, not set up in the way the administrators of the system had assumed they were; usually the dbas thought the [...]]]></description>
			<content:encoded><![CDATA[<p>One of the things that I frequently come across when reviewing SQL Server installations is just how many of them have not been set up with appropriate memory configuration settings, or, as in many cases, not set up in the way the administrators of the system had assumed they were; usually the dbas thought the system was set up to use all e.g. 8GB of RAM, but no changes had been made to the OS or SQL Server configuration, so their (32-bit) SQL Server would only be accessing 2 GB, and reporting that it was using 1.6 GB.</p>
<p>The problem is due in part to the fact that on 32-bit systems (which are still the most prevalent) configuration changes usually have to be made both in SQL Server and at the OS level, and in part to the sprawl of documentation available on configuring SQL Server&#8217;s memory settings, as opposed to a single jumping off point which runs through all the settings and considerations that need to be made.<br />
Add to that the black art of establishing exactly how much memory SQL Server <i>is</i> using (most of the obvious options will only show how much memory the buffer cache is using) and it&#8217;s easy to see why it&#8217;s such a problem area.</p>
<p>In this post I&#8217;ll attempt to clear some of the smog and provide what I hope will be one document which answers most of the questions that arise about configuring SQL Server&#8217;s memory usage.<br />
This discussion will cover configuring memory for SQL Server 2000, SQL Server 2005 and SQL Server 2008 (with the exception of the Resource Governor). This blog assumes an <a href="http://msdn.microsoft.com/en-us/library/ms143685.aspx" target="_blank">edition of SQL Server</a> that is not internally limited in its memory usage.</p>
<h4>32-bit or 64-bit?</h4>
<p>There&#8217;s a big difference between the memory configuration settings between 64-bit SQL Server and 32-bit SQL Server, so it&#8217;s not possible to start a discussion about SQL Server&#8217;s memory management without clarifying whether we are dealing with 32-bit versions or 64-bit versions of the product, as this is key to how much memory SQL Server can address, and (almost as importantly) how it addresses that memory.</p>
<h4>Configuring 32-bit SQL Server</h4>
<p>Until fairly recently 32-bit software was ubiquitous. The server Windows operating systems were 32-bit, your desktop, usually Windows XP was 32-bit. Therefore, I&#8217;ll be focusing a fair bit on 32-bit SQL Server as this is what requires the most configuration, and also where most of the confusion lies.</p>
<p>So, here goes.</p>
<p>The amount of memory a 32-bit process can access is 2^32 or 4294967296 bytes, or 4 GB.<br />
On a 32-bit Windows OS this 4 GB of memory is not all addressable by a single process. Instead, it&#8217;s partitioned by the OS into two address spaces. 2 GB is kept by the OS (commonly referred to as the kernel) and the remaining 2 GB is the user mode address space, or the area each application (process) will have access to. Whilst each user mode process gets 2 GB as its addressable memory range, the kernel mode area is shared between all processes. SQL Server runs in the user mode address space and is bound by default to the 2 GB of memory limit on a 32-bit OS.<br />
This directly addressable memory will hereon be referred to by what it is more commonly known as, the virtual address space or <a href="http://msdn.microsoft.com/en-us/library/aa366912.aspx" target="_blank">VAS</a>.</p>
<p>SQL Server&#8217;s default out-of-the-box memory limit is deliberately set to a very high value of 2147483647 , which basically means all available memory, but as you should now know, there&#8217;s no way it can actually use anywhere near that much memory, particularly on a 32-bit platform.</p>
<p>64-bit operating systems have a far far bigger address space open to them; 8 TB to be exact. Before you run off to your calculator to evaluate 2^64, the answer won&#8217;t be 8TB, but 8TB is what each user mode application gets due to current hardware and OS limitations. The kernel also gets 8TB and this kernel address space is shared by all processes, just as in 32-bit Windows.</p>
<p>Having said all that, I should point out that no current MS Windows OS can address more than 2 TB.</p>
<p>What this means for 64-bit SQL Server is that out of the box, it can address all the memory on a server without any special configuration changes either within SQL Server or at the OS level. The only thing you need to look at is a cap on its memory usage; capping memory usage is covered in the <a href="#maxservermemory">‘max server memory’ and ‘min server memory’</a> section which is further down.</p>
<h4>To /3GB or not to /3GB</h4>
<p>So, as 32-bit applications are natively restricted to a 2 GB VAS, OS configuration tweaks are required to allow access to more than 2 GB, and these are covered next.</p>
<p>The first modification is one that, rather ironically, should be used as a last resort. Ideally, it should be used on the advice of Microsoft Support (PSS).<br />
I&#8217;m choosing to get it out of the way now because the /3GB setting is probably the most well known and most misused.<br />
/3GB allows a 32-bit process to increase its VAS to 3 GB by taking away 1 GB of address space from the kernel, and this is why it&#8217;s a last resort; there&#8217;s no such thing as a free lunch as the removal of 1 GB of addressable memory from the OS can introduce instability. More on that shortly.</p>
<p>To allow a 32-bit process to gain a 3 GB VAS you have to add the <a href="http://msdn.microsoft.com/en-us/library/ms791558.aspx" target="_blank">/3GB</a> switch to the Windows boot.ini file.</p>
<p>As I stated, this can introduce system instability, particularly on Windows 2000 by starving the OS of System Page Table Entries (PTEs). A discussion about PTEs is out of the scope of this blog, but its effects can be dramatic and cause blue-screens. There is scope for manoeuvre here, by adding the <a href="http://support.microsoft.com/kb/316739" target="_blank">/USERVA</a> switch to the boot.ini it is possible to reduce the VAS increase from a straight 3 GB to a lower user-defined amount which will give the OS room to breathe, and thus resolve any instability issues.</p>
<p>The only reason you will be advised by PSS to use /3GB is if you are suffering VAS starvation issues, such as a bloated procedure cache as it can only reside in VAS memory (also see the next section on <strong>MemToLeave</strong>). Only database pages can reside in the part of the SQL Server memory cache (called the buffer cache) that is utilising awe enabled memory.</p>
<h4>MemToLeave</h4>
<p>Because of the inherent address space limitations of a 32-bit process, a certain amount of memory has to be set aside by SQL Server on startup that SQL Server uses for overheads. This memory is set aside in case it all gets used by the buffer cache.<br />
COM objects, extended stored procs, memory allocations exceeding 8K and the memory allocated to the threads SQL Server creates to service e.g. user connections come from a section of memory within the VAS but outside the buffer cache which is typically referred to as the MemToLeave area. This is 384 MB by default on 32-bit SQL and is derived as follows:<br />
<code><br />
256 MB + (0.5 MB x max worker threads [default: 255]) = 384.<br />
</code><br />
0.5 MB is the default thread stack size on 32-bit Windows. 64-bit Windows has a default stack size of 2 MB or 4 MB depending on which 64-bit flavour of Windows you are running (AMD x64 or IA64).<br />
This is why a default 32-bit SQL Server configuration appears to be &#8216;only&#8217; using 1.6 GB out of the 2 GB it is entitled to; 1.6 GB is the buffer cache, and the remainder is MemToLeave.<br />
SQL Server 2005 and beyond uses a <a href="http://msdn.microsoft.com/en-us/library/ms187024.aspx" target="_blank">formula</a> to calculate the max worker threads setting which affects the size of the MemToLeave area.<br />
There is a SQL Server startup parameter (<strong>-g</strong>) which can be used to increase the MemToLeave area, but again, only do this if advised by PSS.</p>
<h4>4 GB of RAM and beyond</h4>
<p>So, we know 32-bit SQL Server can use 2 GB out of the box, and up to 3 GB (with an OS tweak that is best avoided, if at all possible).<br />
However, 32-bit SQL Server can benefit from much more memory than 3 GB with the help of OS and SQL Server configuration modifications which will be covered next.</p>
<h4>/PAE</h4>
<p>To address more than 4 GB of RAM on 32-bit Windows, the OS needs to have the <a href="http://msdn.microsoft.com/en-us/library/ms791485.aspx" target="_blank">/PAE</a> switch added to the boot.ini file, although if your system supports hot-swappable memory you won&#8217;t need to add this as Windows should automatically be able to see the additional memory. If you&#8217;re not sure, take a look at how much memory the OS can see via System properties; if you have more than 4 GB installed and the OS is only showing 4 GB, review your boot.ini settings. I&#8217;m not mentioning specific Windows versions because the /PAE switch applies to all current 32-bit versions of Windows.</p>
<h4><em>Both</em> /PAE and /3GB</h4>
<p>Some systems have both /3GB and /PAE enabled.<br />
This is fine as long as the system does not have more than 16 GB of RAM. Add any more memory and <a href="http://support.microsoft.com/kb/283037" target="_blank">Windows will not recognise it</a> because of the overhead required to manage the additional memory.</p>
<h4>Clusters</h4>
<p>No special configuration settings regarding memory settings are required for a cluster, but I thought I better mention clusters specifically because you won&#8217;t believe how many installations there are out there where there are different settings on different nodes within the same cluster.<br />
So, make sure any OS setting changes like /3GB or /PAE are consistently applied across <em>all</em> nodes.</p>
<h4>Enable AWE</h4>
<p>After configuring the OS, you&#8217;ll need to configure SQL Server by enabling AWE (Address Windowing Extensions). AWE is in essence a technique for &#8216;paging&#8217; in sections of memory beyond the default addressable range.<br />
AWE can be enabled in SQL Server using Query Analyzer/SQL Server Management Studio (SSMS) via the following statements:<br />
<code><br />
sp_configure 'show advanced options', 1<br />
RECONFIGURE<br />
GO<br />
sp_configure 'awe enabled', 1<br />
RECONFIGURE<br />
GO<br />
</code><br />
AWE enablement is not a dynamic option and will require a SQL Server restart.</p>
<h4><a id="maxservermemory"></a>&#8216;max server memory&#8217; and &#8216;min server memory&#8217;</h4>
<p>The more memory you give SQL Server, the greater the need to set an upper limit on how much it uses. When you start SQL Server it&#8217;ll ramp up its memory usage until it has used up all the memory it can access, which will either be an internal OS limit or a SQL Server configured limit.<br />
A 32-bit SQL Server instance will therefore grab up to 2 GB if the workload demands it and it is on default settings.<br />
An awe enabled SQL Server instance will go on using up all the memory on the system if the workload is there and an upper limit on its memory usage is not set.</p>
<p>Setting a limit has the double-benefit of not starving the OS of resources and avoiding &#8216;Out of memory&#8217; errors which can occur on SQL Server systems that may have a lot of memory. The latter (rather contradictory) situation can arise because SQL Server will try and allocate more memory when it is already at the system limit (if no upper limit has been set via the &#8216;max server memory&#8217; setting) instead of freeing up memory it is already using.</p>
<h4>Multiple instances</h4>
<p>A third reason to set an upper limit is if you have more than one SQL Server instance installed on a single host, as this will stop the instances competing for memory.<br />
Allocate a high enough &#8216;max server memory&#8217; limit to each instance to allow it to do its job without running into memory starvation issues, whilst reserving the bulk of the memory for higher priority instances (if any) <em>and</em> the OS.<br />
This is where benchmarking comes in handy.</p>
<p>To set a max server memory limit of 12 GB via Query Analyzer/SSMS:<br />
<code><br />
sp_configure 'max server memory', 12288<br />
RECONFIGURE<br />
GO<br />
</code></p>
<p>SQL Server ramps up its memory usage because by default it is set to use no memory on startup. This is controlled by the &#8216;min server memory&#8217; setting. Specifying this to a higher value has the benefit of reserving a set amount of memory for SQL Server from the off which can provide a slight performance benefit, especially on busy systems. It&#8217;s actually not uncommon to see &#8216;min server memory&#8217; and &#8216;max server memory&#8217; set to the same value, to reserve all of SQL Server&#8217;s memory straight away. The downside is SQL Server will take slightly longer to start up than if &#8216;min server memory&#8217; was set to a low value.</p>
<h4>SQL doesn&#8217;t really release memory</h4>
<p>This will probably get me into a bit of trouble, as there are KBs that clearly state that SQL Server releases memory when the OS is under pressure.<br />
True, but only when it&#8217;s under a <strong>lot</strong> of pressure (although this is getting better with each version of SQL Server), and by then it&#8217;s often too late as the system is usually in such a degraded state by that stage that a restart is necessary.<br />
That&#8217;s why it&#8217;s vital to set an upper limit on its memory usage via the &#8216;max server memory&#8217; setting.</p>
<h4>Memory corruption</h4>
<p>Slightly off-topic, but this is an appropriate place to bring this up.<br />
Certain builds of <a href="http://support.microsoft.com/?kbid=838647" target="_blank">Windows 2000</a> and <a href="http://support.microsoft.com/Default.aspx?kbid=834628" target="_blank">Windows Server 2003</a> contained a potentially serious memory corruption problem which affected SQL Server more than other applications, mainly because there are few applications that run on Windows that can utilise the amount of memory SQL Server does.<br />
It&#8217;s difficult to overstate the problems this can cause, so make sure you&#8217;re on the appropriate Windows Service Packs if you&#8217;re running SQL Server on a PAE enabled system.<br />
Another issue that arose in <a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;899761" target="_blank">SQL Server 2000 SP4</a> was a bug that meant SQL Server only saw half the memory on awe enabled systems, although it was identified quickly and the hotfix for this was placed alongside the SP4 download.</p>
<h4>32-bit SQL Server on 64-bit Windows</h4>
<p>If you 32-bit SQL Server on 64-bit Windows the SQL Server process can access the entire 4 GB VAS.</p>
<h4>Checking SQL Server&#8217;s memory usage</h4>
<p>This is another area where there is lot of confusion, so below is a run-through of the most common methods for confirming SQL Server&#8217;s memory usage.</p>
<h4>Ignore Task Manager</h4>
<p>If you have an awe enabled SQL Server instance, do not rely on Task Manager to display memory usage as it does not show the AWE memory a process is using, so the memory usage figure it presents for the SQL Server process (sqlservr.exe) will be incorrect.</p>
<h4>DBCC MEMORYSTATUS</h4>
<p>Running the above command outputs the memory usage of SQL Server including how that memory is allocated, so unless you need to know how and where that memory is being used, the output it generates can be a bit bewildering. The important bits of this output pertaining to SQL Server&#8217;s total memory usage are as follows:<br />
<code><br />
Buffer Counts                  Buffers<br />
------------------------------ --------------------<br />
Committed                      3872<br />
Target                         65536<br />
Hashed                         2485<br />
Stolen Potential               60972<br />
External Reservation           0<br />
Min Free                       64<br />
Visible                        65536<br />
Available Paging File          702099<br />
</code><br />
The key figures in the above output are committed, target and hashed.<br />
Committed is the amount of memory in use by the buffer cache and includes AWE pages.<br />
Target is how big SQL Server wants the buffer to grow, so you can infer from this whether SQL Server wants more memory or is releasing memory.<br />
There&#8217;s an excellent KB on interpreting all the output <a href="http://support.microsoft.com/?id=271624" target="_blank">INF: Using DBCC MEMORYSTATUS to Monitor SQL Server Memory Usage</a> for SQL Server 2000 and <a href="http://support.microsoft.com/kb/907877/en-us" target="_blank">How to use the DBCC MEMORYSTATUS command to monitor memory usage on SQL Server 2005</a>.<br />
Edit (05/02/09): Remember the buffer count numbers refer to pages of memory which are 8K in SQL Server</p>
<h4>System Monitor (perfmon)</h4>
<p>Perfect way to get a quick reference on exactly how much memory SQL Server is using at that moment. Start System Monitor and add the SQL Server: Memory Manager: Total Server Memory (KB) counter.</p>
<p>Replace &#8220;SQL Server&#8221; with MSSQL$ and the name of the named instance if it&#8217;s not a default instance, e.g. MSSQL$INSTANCE1.</p>
<h4>&#8216;Total&#8217; memory usage</h4>
<p>When trying to establish exactly how much memory SQL Server is using it&#8217;s not just the buffer pool memory you have look at, but the MemToLeave area as well. The key point to bear in mind here is that it&#8217;s not only SQL Server that can make allocations from this latter area of memory but third party processes as well, which can make it impossible to precisely account for SQL Server&#8217;s absolute memory usage, contrary to some myths out there about calculating SQL Server&#8217;s memory usage via e.g. DBCC MEMORYSTATUS, as such methods can only account for SQL Server&#8217;s <em>own</em> memory allocations and <em>not</em> allocations by foreign processes.</p>
<h4>NUMA</h4>
<p>I couldn&#8217;t really finish this blog without at least a passing reference to NUMA, or Non-Uniform Memory Architecture. <a href="http://msdn.microsoft.com/en-us/library/ms178144.aspx" target="_blank">NUMA</a> enabled systems allow memory to be partitioned between CPUs on multi-CPU systems. SQL Server can also affinitise NUMA nodes to tcp/ip ports. If you have high spec non NUMA capable hardware SQL Server can take advantage of the scalability enhancements NUMA provides by simulating NUMA-like behaviour using a technique referred to as <a href="http://msdn.microsoft.com/en-us/library/ms345357.aspx" target="_blank">soft-NUMA</a>.</p>
<h4>64-bit</h4>
<p>I mentioned at the start of this post that all you have to worry about for 64-bit SQL Server is setting a max memory limit as SQL Server can access all the memory current Windows operating systems can support, and 8 TB in total. That&#8217;s mostly true, with the exception of a certain privilege that the SQL Server service account needs, and that&#8217;s the &#8216;Lock Pages in Memory&#8217; privilege.<br />
This privilege is vital as it prevents the OS from paging out SQL Server memory to the swap file.<br />
With the introduction of SQL Server 2005, this right was restricted on 64-bit Windows to <a href="http://support.microsoft.com/kb/918483/en-us" target="_blank">only take effect on Enterprise Editions of SQL Server</a>, so if you&#8217;re wondering why your huge new multi-gigabyte multi-core 64-bit system is paging like crazy, this might be why. [Edit: This has finally been reversed for both SQL Server 2005 and SQL Server 2008 Standard Editions: <a href="http://blogs.msdn.com/psssql/archive/2009/04/24/sql-server-locked-pages-and-standard-sku.aspx" target="_blank">http://blogs.msdn.com/psssql/archive/2009/04/24/sql-server-locked-pages-and-standard-sku.aspx</a><br />
Whilst we&#8217;re on the subject of paging on 64-bit SQL Server systems, take a look at the following KB:<br />
<a href="http://support.microsoft.com/default.aspx/kb/918483" target="_blank">How to reduce paging of buffer pool memory in the 64-bit version of SQL Server 2005</a> which covers issues a number of issues that cause SQL Server&#8217;s (Standard or Enterprise editions) memory to be paged out.</p>
<h4>In summary&#8230;</h4>
<p>The table below describes how much memory SQL Server can use, and assumes an edition of SQL Server that has no internal limitations as to how much memory it can use, e.g. Express and Workgroup editions are limited to 1 GB and 3GB respectively.</p>
<table border="1" width="446">
<tbody>
<tr>
<td rowspan="2" align="left">SQL Server type</td>
<td colspan="3">Installed physical memory</td>
</tr>
<tr>
<td colspan="2">Up to 4GB</td>
<td>More than 4GB (/PAE enabled<sup>1</sup>)</td>
</tr>
<tr>
<td rowspan="2" align="left">32-bit SQL Server</td>
<td align="left">Default memory usage</td>
<td>With /3GB<sup>2</sup></td>
<td rowspan="2" align="center">All available RAM<sup>3</sup></td>
</tr>
<tr>
<td>2 GB</td>
<td>3 GB</td>
</tr>
<tr>
<td align="left">64-bit SQL Server</td>
<td colspan="3" align="center">All available RAM<sup>3</sup></td>
</tr>
<tr>
<td colspan="4"><span style="font-size: x-small;"><br />
<sup>1</sup> Not all 32-bit systems now need to have /PAE explicitly set in boot.ini for the OS to see more than 4 GB of RAM.<sup>2</sup> Assuming /USERVA switch has not been used to tune memory usage to between 2 GB and 3 GB.<sup>3</sup> Assuming <code>'max server memory'</code> is left on defaults, otherwise SQL Server will use no more memory than that stipulated by the <code>'max server memory'</code> setting.  </p>
<p> </span></td>
</tr>
</tbody>
</table>
<p>When I started this blog I wanted to keep it as short and succinct as possible, but there&#8217;s a lot more to configuring SQL Server&#8217;s memory usage than simply setting a &#8216;max server memory&#8217; limit. Configuring SQL Server&#8217;s memory settings can be quite a complex undertaking, especially in a 32-bit environment. It&#8217;s not easy to cover all the pertinent points without branching off and describing the different areas of its memory architecture, although I&#8217;ve tried to provide the relevant information without going into too much detail.<br />
Hopefully, this blog has clarified how to configure SQL Server&#8217;s memory usage and provided enough information to answer most memory configuration related questions.</p>
<h5>Useful links</h5>
<p><a href="http://blogs.msdn.com/psssql/archive/2009/05/15/how-it-works-dbcc-memorystatus-locked-pages-allocated-and-singlepageallocator-values.aspx" target="_blank">How It Works: DBCC MemoryStatus Locked Pages Allocated and SinglePageAllocator Values</a></p>
<p><a href="http://support.microsoft.com/kb/271624" target="_blank">INF: Using DBCC MEMORYSTATUS to Monitor SQL Server Memory Usage</a></p>
<p><a href="http://blogs.msdn.com/psssql/archive/2009/08/26/come-on-64bit-so-we-can-leave-the-mem.aspx" target="_blank">MemToLeave and 64-bit SQL Server</a><br />
<script type="text/javascript"><!--
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
// --></script><br />
<script type="text/javascript"><!--
var pageTracker = _gat._getTracker("UA-5764551-1");
pageTracker._trackPageview();
// --></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eraofdata.com/blog/2008/10/sql-server-memory-configuration/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
