<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Pankaj Mittal [MSFT]</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/pamitt/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/pamitt/feed.xml</link>
    <description />
    <lastBuildDate>Tue, 16 Apr 2019 05:22:01 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>SQL Server 2005 and 2008 versions may not detect all available processors on a machine with more than 64 logical processors</title>
      <link>https://docs.microsoft.com/archive/blogs/pamitt/sql-server-2005-and-2008-versions-may-not-detect-all-available-processors-on-a-machine-with-more-than-64-logical-processors</link>
      <pubDate>Wed, 02 Nov 2011 22:54:22 GMT</pubDate>
      <dc:creator><![CDATA[Pankaj Mittal]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/pamitt/2011/11/02/sql-server-2005-and-2008-versions-may-not-detect-all-available-processors-on-a-machine-with-more-than-64-logical-processors/</guid>
      <description><![CDATA[Scenario: I came across this scenario recently where a SQL Server 2005 instance installed on Windows...]]></description>
      <content:encoded><![CDATA[<p><strong>Scenario:</strong> I came across this scenario recently where a SQL Server 2005 instance installed on Windows Server 2008 R2, wasn’t detecting all available logical processors after Hyperthreading was enabled on the server. The machine in question had 4 sockets with each socket representing a NUMA node and each socket had 10 cores on it - so there were a total of 40 cores on the server. Without Hyperthreading turned on, SQL Server detected all 40 logical processors. However, after Hyperthreading was enabled, SQL server instance detected only 20 logical processors, although there were a total of 80 logical processors available on the server. Here is what we noticed in the SQL Server error logs before and after turning on Hyperthreading -</p>  <blockquote>   <p><strong><u>With Hyperthreading turned <font color="#ff0000">OFF</font></u></strong></p>    <p>2011-10-27 12:37:32.74 Server&#160;&#160;&#160;&#160;&#160; <font style="background-color: #ffff00">Detected 40 CPUs.</font> This is an informational message; no user action is required.</p>    <p>2011-10-27 12:37:32.94 Server&#160;&#160;&#160;&#160;&#160; Large Page Extensions enabled.</p>    <p>2011-10-27 12:37:32.94 Server&#160;&#160;&#160;&#160;&#160; Large Page Granularity: 2097152 </p>    <p>2011-10-27 12:37:32.98 Server&#160;&#160;&#160;&#160;&#160; Large Page Allocated: 32MB </p>    <p>2011-10-27 12:37:33.03 Server&#160;&#160;&#160;&#160;&#160; Large Page Allocated: 32MB </p>    <p>2011-10-27 12:37:33.42 Server&#160;&#160;&#160;&#160;&#160; Using locked pages for buffer pool.</p>    <p>2011-10-27 12:37:33.79 Server&#160;&#160;&#160;&#160;&#160; Large Page Allocated: 32MB </p>    <p>2011-10-27 12:37:33.90 Server&#160;&#160;&#160;&#160;&#160; Large Page Allocated: 32MB </p>    <p>2011-10-27 12:37:34.17 Server&#160;&#160;&#160;&#160;&#160; Using dynamic lock allocation.&#160; Initial allocation of 2500 Lock blocks and 5000 Lock Owner blocks per node.&#160; This is an informational message only.&#160; No user action is required.</p>    <p>2011-10-27 12:37:34.19 Server&#160;&#160;&#160;&#160;&#160; Lock partitioning is enabled.&#160; This is an informational message only. No user action is required.</p>    <p>2011-10-27 12:37:34.53 Server&#160;&#160;&#160;&#160;&#160; Multinode configuration: node 0: CPU mask: 0x00000000000ffc00 Active CPU mask: 0x00000000000ffc00. This message provides a description of the NUMA configuration for this computer. This is an informational message only. No user action is required.</p>    <p>2011-10-27 12:37:34.60 Server&#160;&#160;&#160;&#160;&#160; Multinode configuration: node 1: CPU mask: 0x00000000000003ff Active CPU mask: 0x00000000000003ff. This message provides a description of the NUMA configuration for this computer. This is an informational message only. No user action is required.</p>    <p>2011-10-27 12:37:34.65 Server&#160;&#160;&#160;&#160;&#160; Multinode configuration: node 2: CPU mask: 0x000000003ff00000 Active CPU mask: 0x000000003ff00000. This message provides a description of the NUMA configuration for this computer. This is an informational message only. No user action is required.</p>    <p>2011-10-27 12:37:34.70 Server&#160;&#160;&#160;&#160;&#160; Multinode configuration: node 3: CPU mask: 0x000000ffc0000000 Active CPU mask: 0x000000ffc0000000. This message provides a description of the NUMA configuration for this computer. This is an informational message only. No user action is required </p> </blockquote>  <blockquote>   <p><strong><u>With Hyperthreading turned <font color="#ff0000">ON</font></u></strong></p>    <p>2011-10-27 13:13:50.31 Server&#160;&#160;&#160;&#160; <font style="background-color: #ffff00">Detected 20 CPUs</font>. This is an informational message; no user action is required.</p>    <p>2011-10-27 13:13:50.48 Server&#160;&#160;&#160;&#160;&#160; Large Page Extensions enabled.</p>    <p>2011-10-27 13:13:50.48 Server&#160;&#160;&#160;&#160;&#160; Large Page Granularity: 2097152 </p>    <p>2011-10-27 13:14:04.68 Server&#160;&#160;&#160;&#160;&#160; Large Page Allocated: 32MB </p>    <p>2011-10-27 13:14:07.87 Server&#160;&#160;&#160;&#160;&#160; Using locked pages for buffer pool.</p>    <p>2011-10-27 13:14:12.25 Server&#160;&#160;&#160;&#160;&#160; Using dynamic lock allocation.&#160; Initial allocation of 2500 Lock blocks and 5000 Lock Owner blocks per node.&#160; This is an informational message only.&#160; No user action is required.</p>    <p>2011-10-27 13:14:12.28 Server&#160;&#160;&#160;&#160;&#160; Lock partitioning is enabled.&#160; This is an informational message only. No user action is required.</p> </blockquote>  <p><strong>Cause and Explanation:</strong> A new feature introduced in Windows Server 2008 R2 called “Processor Groups” was responsible for this behavior. Existing applications such as SQL Server 2005 and 2008, which were released prior to the release of Windows Server 2008 R2, can’t take advantage of this feature thus causing the issue</p>  <p><strong>What is a Processor group?</strong> In Windows Server 2008 R2, support for systems that have more than 64 logical processors is based on the concept of a processor group, which is a static set of up to 64 logical processors that is treated as a single scheduling entity. Processor groups are numbered starting with 0. Systems with fewer than 64 logical processors always have a single group, Group 0. The operating system minimizes the number of groups in a system. For example, a system with 128 logical processors would have two processor groups with 64 processors in each group, not four groups with 32 logical processors in each group. Processor Group is a new concept in Windows Server 2008 R2 and is not supported by Windows Server 2008, Windows Vista and Windows Server 2003.</p>  <p><strong>Why does Processor Groups affect SQL Server 2005 and 2008 instances?</strong> Following three factors combined together would cause Processor Groups to affect SQL Server 2005 and 2008 instances - </p>  <ul>   <li>Both SQL Server 2005 and SQL Server 2008 were released prior to the release of Windows Server 2008 R2 and therefore these versions do not utilize the new APIs that were released to take advantage of Processor Groups in Windows Server 2008 R2. </li>    <li>Windows Server 2008 R2 creates uneven Processor Groups on machines that has &gt; 64 logical processors. This is because the algorithms implemented in Windows 2008 R2 originally were looking to create as little as possible processor groups and keep the individual processor groups as large as possible. Since there is only one Processor Group on servers that has &lt;= 64 processors, no problems are expected on such machines. However, if a machine has more than 64 logical processors, Windows Server 2008 R2 will create more than one processor group and SQL Server 2005/2008 will be bound to any of the groups on service restart. In our scenario, after Hyperthreading was turned on, the machine had 80 Logical processors and 4 NUMA nodes, which were divided into 2 groups by the Windows Server 2008 R2 operating system. The first group had 3 NUMA nodes and hence 60 logical processors in it while the second groups had 1 NUMA node with 20 logical processors in it. When SQL instance was restarted, it was bound to the first group with 20 logical processors in it. </li>    <li>Windows Server 2008 R2 will randomly assign such applications that are not aware of Processor Groups to one of the existing Processor Groups. </li> </ul>  <p><strong>Workaround/Fix:</strong> A couple of things to consider -</p>  <ul>   <li>How to utilize &gt; 64 logical processors for SQL instances? SQL Server 2005/2008 versions cannot utilize more than 64 logical processors and therefore if you would like your SQL server instance to be able to utilize more than 64 processors, the only option is to upgrade to SQL Server 2008 R2. </li>    <li>How to force a SQL Server instance to use the Processor Group with maximum number of logical processors in it? For example, on a machine with 96 CPUs you could have two processor groups, Group # 1 with 64 CPUS and Group # 2 with 32 CPUs.&#160; If you have SQL Server 2005/2008 Enterprise installed it can use up to 64 CPUs.&#160; In this scenario, you don’t want&#160; your SQL Server instance to be associated with Group # 2 that has 32 CPUs. If you would like your SQL Server 2005 and 2008 instances to be able to utilize the processor group that has maximum available processors in it, you can use the sc.exe utility as described in <a href="https://blogs.msdn.com/b/psssql/archive/2009/06/16/sql-server-on-windows-7-windows-2008-r2-with-more-than-64-cpus.aspx" target="_blank">this blog article</a> </li>    <li>How to resolve issues with uneven processor group assignment? Apply Windows Server 2008 R2 hotfix as described in <a href="https://support.microsoft.com/kb/2510206" target="_blank">KB 2510206</a> </li>    <li>How to manually assign processor groups to specific NUMA nodes? Follow instructions in <a href="https://support.microsoft.com/kb/2506384" target="_blank">KB 2506384</a> </li> </ul>  <p>Additional References:</p>  <ul>   <li><a href="https://msdn.microsoft.com/en-us/library/dd405503%28VS.85%29.aspx" target="_blank">Processor Groups</a></li>    <li><a href="https://blogs.msdn.com/b/saponsqlserver/archive/2011/10/08/uneven-windows-processor-groups.aspx" target="_blank">Uneven Windows Processor Groups</a></li>    <li><a href="https://support.microsoft.com/kb/2510206" target="_blank">Performance issues when more than 64 logical processors are used in Windows Server 2008 R2</a></li>    <li><a href="https://support.microsoft.com/kb/2506384" target="_blank">How to manually configure K-Group assignment on multiprocessor machines</a></li>    <li><a href="https://blogs.msdn.com/b/saponsqlserver/archive/2011/04/20/changes-in-affinity-settings-of-sql-server-2008-r2-to-support-gt-64-logical-processors.aspx" target="_blank">Changes in affinity settings of SQL Server 2008 R2 to support &gt; 64 Logical Processors</a></li>    <li><a href="https://blogs.msdn.com/b/saponsqlserver/archive/2010/09/28/windows-2008-r2-groups-processors-sockets-cores-threads-numa-nodes-what-is-all-this.aspx" target="_blank">Windows 2008 R2 - Groups, Processors, Sockets, Cores Threads, NUMA nodes what is all this?</a></li>    <li><a href="https://msdn.microsoft.com/en-us/library/ee210585.aspx" target="_blank">ALTER SERVER CONFIGURATION</a></li>    <li><a href="https://blogs.msdn.com/b/psssql/archive/2009/06/16/sql-server-on-windows-7-windows-2008-r2-with-more-than-64-cpus.aspx" target="_blank">SQL Server on Windows 7 / Windows 2008 R2 with more than 64 CPUs</a></li>    <li><a href="https://blogs.msdn.com/b/saponsqlserver/archive/2011/04/18/how-many-logical-processors-does-sql-server-2008-r2-enterprise-edition-support.aspx" target="_blank">How many logical processors does SQL Server 2008 R2 Enterprise Edition support</a></li> </ul>  <p>Would love to hear thoughts from folks who have experienced this issue firsthand. Please leave your feedback below if you have anything to share</p>  <p>Cheers!</p>]]></content:encoded>
    </item>
    <item>
      <title>Considerations with using Mount Points in SQL Server 2008/R2 Failover Clusters</title>
      <link>https://docs.microsoft.com/archive/blogs/pamitt/considerations-with-using-mount-points-in-sql-server-2008r2-failover-clusters</link>
      <pubDate>Sat, 29 Oct 2011 01:09:43 GMT</pubDate>
      <dc:creator><![CDATA[Pankaj Mittal]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/pamitt/2011/10/29/considerations-with-using-mount-points-in-sql-server-2008r2-failover-clusters/</guid>
      <description><![CDATA[Recently I was testing with using Mount Point in SQL Server 2008 failover cluster installation and...]]></description>
      <content:encoded><![CDATA[<p>Recently I was testing with using Mount Point in SQL Server 2008 failover cluster installation and came across some interesting issues, which I wanted to share for the benefits of my customers. </p>  <p><strong>Installation Failure:</strong></p>  <blockquote>   <p><strong>Scenario:</strong> The first time I tried the installation with mount points, the installation failed with the following error – “Wait on the Database Engine recovery handle failed. Check the SQL Server error log for potential causes”. Consider the following scenario - </p>    <ul>     <li>During the installation, on the Data Directories page, you select separate mount points for system, data, log, Tempdb and Backup directories, but do not make any changes to the default folder structures</li>      <li>The SQL Server service account does not have sufficient permissions on the mounted drive.</li>      <li>The installation fails with the following error message – “Wait on the Database Engine recovery handle failed. Check the SQL Server error log for potential causes”</li>   </ul>    <p><strong>Cause:</strong> I discovered that this is a known issue in SQL Server 2008 / R2. If you proceed with the default folder hierarchy after selecting your Mount Points, the default location points to the root of the mounted volumes. If your SQL Server service does not have sufficient permissions to write to the root of the mounted volumes, installation will fail. </p>    <p><strong>Workarounds:</strong> You can use one of the following two workarounds to fix this problem -</p>    <ul>     <li>Workaround # 1: Change the default folder path to use a sub directory under the root drive. For example, if <strong>T:\mountX</strong> is a mount points of volume X. Then specify <strong>T:\mountX\data</strong> as the directory to store database files.</li>      <li>Workaround # 2: Grant SQL server service account full control permissions to the root volume, such as <strong>mountX</strong> by using the disk management console</li>   </ul>    <p><strong>References:</strong></p>    <ul>     <li><a href="https://connect.microsoft.com/SQLServer/feedback/details/569895/installation-of-sql-server-2008-r2-on-mount-points-fails" target="_blank">Installation of SQL Server 2008 R2 on Mount Points fails</a></li>      <li><a href="https://getyouriton.blogspot.com/2009/08/serious-gotchas-with-mounted-drives-or.html" target="_blank">Gotchas with mounted drives or mount points on Microsoft Windows Server</a> </li>      <li><a href="https://support.microsoft.com/kb/2216461" target="_blank">SQL Server 2008 Analysis Services failover cluster setup fails to install on a Windows Server 2008-based cluster mount point</a></li>      <li><a href="https://support.microsoft.com/default.aspx?scid=kb;EN-US;955863" target="_blank">Microsoft SQL Server 2008 may not start after you install it in the root directory of an NTFS volume</a></li>   </ul> </blockquote>  <p><strong>Mount Point Dependency Issues:</strong></p>  <blockquote>   <p><strong>Scenario:</strong> After you have successfully installed a SQL Server 2008/R2 failover cluster with mount points, data corruption could occur because of incorrect resource dependencies in the SQL server instance group</p>    <p><strong>Cause:</strong> This is a known issue in SQL Server 2008/R2. SQL Server setup will not create the required dependencies automatically and you will need to add the required dependencies manually.</p>    <p><strong>Solution:</strong> Add the following dependencies manually -</p>    <ul>     <li>Make sure that each of the mounted volume is dependent on the root (or host) drive.</li>      <li>Make sure that SQL Server is dependent on not just the root (or host) drive, but also on each of the mounted volumes.</li>   </ul>    <p><strong>References:</strong></p>    <ul>     <li><a href="https://support.microsoft.com/kb/956008" target="_blank">After you install a SQL Server 2008 failover cluster in a disk that contains a mounted volume, no dependencies are created between the mounted volume and the disk</a></li>      <li><a href="https://support.microsoft.com/kb/835185" target="_blank">Failover cluster resource dependencies in SQL Server</a></li>   </ul> </blockquote>  <p><strong>Best Practices when using Mount Points in SQL Server -</strong> </p>  <blockquote>   <p>Follow the following best practices when using mount points with SQL Server standalone and failover cluster instances -</p> </blockquote>  <blockquote>   <ul>     <li>Use the root (host) volume exclusively for mount points. The root volume is the volume that is hosting the mount points. This greatly reduces the time that it takes to restore access to the mounted volumes if you have to run a chkdsk. This also reduces the time that it takes to restore from backup on the host volume.</li>      <li>If you use the root (host) volume exclusively for mount points, the size of the host volume only has to be several MB. This reduces the probability that the root volume will be used for anything other than the mount points. </li>      <li>During failover cluster installation, use subdirectories under the root of mounted volumes to store database and backup files. For example, say you have a mounted volume F:\SQL1. This is the root of the mount point and you shouldn't use this location directly to store your database files. You should instead create a subdirectory/subfolder such as d:\SQL1\USERDATA and use this location to store your database files.</li>      <li>Add missing dependencies after installing SQL Server 2008 / R2 failover cluster with mount points. Make sure that each of the mounted volume is dependent on the root (or host) drive. Additionally, make sure that SQL Server is dependent on not just the root (or host) drive, but also on each of the mounted volumes.</li>      <li>If configuring MSDTC on failover cluster, do not use mount points as storage for the MSDTC service. MSDTC currently does not supported mount points.</li>   </ul>    <p><strong>References:</strong></p>    <ul>     <li><a href="https://support.microsoft.com/kb/280297" target="_blank">How to configure volume mount points on a Microsoft Cluster Server in Windows Server 2003</a></li>      <li><a href="https://support.microsoft.com/kb/947021" target="_blank">How to configure volume mount points on a server cluster in Windows Server 2008</a></li>      <li><a href="https://support.microsoft.com/kb/819546" target="_blank">SQL Server support for mounted volumes</a></li> <!--EndFragment--></ul> </blockquote>  <p>Hope you find this useful!   </p>]]></content:encoded>
    </item>
    <item>
      <title>How to use SQLDiag, SQLNexus and PAL tools to analyze performance issues in SQL Server</title>
      <link>https://docs.microsoft.com/archive/blogs/pamitt/how-to-use-sqldiag-sqlnexus-and-pal-tools-to-analyze-performance-issues-in-sql-server</link>
      <pubDate>Fri, 25 Feb 2011 11:42:32 GMT</pubDate>
      <dc:creator><![CDATA[Pankaj Mittal]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/pamitt/2011/02/25/how-to-use-sqldiag-sqlnexus-and-pal-tools-to-analyze-performance-issues-in-sql-server/</guid>
      <description><![CDATA[SQLDiag, SQLNexus and PAL (Performance Analysis of Logs) tools can collectively be used to analyze...]]></description>
      <content:encoded><![CDATA[<p align="justify">SQLDiag, SQLNexus and PAL (Performance Analysis of Logs) tools can collectively be used to analyze performance problems in SQL Server. SQLDiag is a data collection utility, while SQLNexus and PAL tools can be used to analyze data collected by the SQLDiag utility. Ever used the <a href="https://support.microsoft.com/kb/830232" target="_blank">PSSDiag</a> utility that was (and is still) used by Microsoft support to assist customers in troubleshooting SQL Server performance issues? SQLDiag is the same tool as PSSDiag and is available out of the box in SQL Server 2005 and all later versions. PSSDiag utility is still available as a separate <a href="https://www.microsoft.com/downloads/en/details.aspx?FamilyID=5564386a-28c2-4483-8293-76fff67b9eb3&amp;amp;amp;amp;amp;amp;amp;amp;DisplayLang=en" target="_blank">download</a> on the Microsoft website.&#160; Both SQLDiag and PSSDiag tools are similar in all aspects, except for the user interface that comes with the PSSDiag utility.</p>  <p align="justify">The SQLNexus tool can be used to analyze SQL server performance data such as blocking, wait stats and SQL traces. The PAL tool can be used to analyze the Perfmon counters collected in a .BLG file . Both of these tools are open source tools and are publicly available for download on the <a href="https://www.codeplex.com">www.codeplex.com</a> website. Please see the SQL Server Downloads section on the <a href="https://blogs.msdn.com/b/pamitt/" target="_blank">Blog Home</a> for the download location of these tools and their pre-requisites components.</p>  <p><strong>What sort of diagnostic data can I collect by using the SQLDiag tool?</strong></p>  <p align="justify">The SQLDiag utility is capable of collecting the following sets of diagnostic information -</p>  <ul>   <li>     <div align="justify">Windows Event Logs (Application, System, and Security) in .CSV files</div>   </li>    <li>     <div align="justify">System Monitor (aka Perfmon) performance counters in .BLG file/s</div>   </li>    <li>     <div align="justify">SQL Server Profiler traces in .TRC file/s</div>   </li>    <li>     <div align="justify">SQL Server Blocking chains in a _BLK.TRC file</div>   </li>    <li>     <div align="justify">SQL Server error logs, configuration, point-in-time snapshots of several DMVs in a .OUT file</div>   </li>    <li>     <div align="justify">System information via the Msinfo32 utility in a .TXT file</div>   </li> </ul>  <p align="justify">Additionally, SQLDiag can be configured to collect custom data such as the output of a .CMD batch file or a .EXE utility.</p>  <p><strong>What sort of performance issues can I troubleshoot with the SQLDiag, SQLNexus and the PAL tools?</strong></p>  <p align="justify">SQLDiag, SQLNexus and PAL tools can be used to troubleshoot a variety of performance issues in SQL Server. I have used these tools on many occasions to assist my customers while troubleshooting performance problems, mostly in the following scenarios - </p>  <ul>   <li>     <div align="justify">Intermittent CPU spikes on the SQL Server box</div>   </li>    <li>     <div align="justify">Performance degradation during specific hours (say between 2:00 PM and 4:00 PM everyday)</div>   </li>    <li>     <div align="justify">Troubleshooting Blocking chains in SQL Server</div>   </li>    <li>     <div align="justify">Analyzing overall performance bottlenecks in SQL Server using wait statistics</div>   </li>    <li>     <div align="justify">Identifying top queries by CPU, Duration, Reads and Writes during a specific timeframe (say between 9:00 AM and 5:00 PM)</div>   </li>    <li>     <div align="justify">Identifying specific databases and logins (sessions) that are consuming the maximum CPU and IO on a SQL sever instance during a specific timeframe (say between 9:00 AM and 5:00 PM)</div>   </li>    <li>     <div align="justify">Troubleshooting performance issues with a batch job that runs every night during a specific timeframe (say between 2:00 AM and 4:00 AM)</div>   </li>    <li>     <div align="justify">Comparing SQL server performance for various tests in a lab testing scenario – say you are seeing performance and scalability issues with a 150 user test, while the 50 user test runs fine.</div>   </li> </ul>  <p align="justify">Of course, this is not an exhaustive list&#160; that describes the application of these tools and you will find them useful in troubleshooting other issues in SQL Server as well. In my future posts, I will attempt to describe how I used these tools to troubleshoot some of the above scenarios.</p>  <p><strong>How are these tools different from&#160; the inbuilt features such as Standard Reports and Activity Monitor in Management Studio ?</strong></p>  <p align="justify">That’s a great question! Why even bother to learn these additional tools when we have introduced so many great features in SQL Server 2005 such as <a href="https://blogs.msdn.com/b/buckwoody/archive/2007/10/09/sql-server-management-studio-standard-reports-overview.aspx" target="_blank">Standard Reports</a>, and <a href="https://msdn.microsoft.com/en-us/library/cc879320.aspx" target="_blank">Activity Monitor</a> in Management Studio reports and <a href="https://msdn.microsoft.com/en-us/library/ms188754.aspx" target="_blank">Dynamic Management Views (DMVs)</a>. The answer is – it depends! Each of these tools have their own strengths and can be useful in a specific scenario depending on the problem you are trying to troubleshoot. SQLDiag, SQLNexus and PAL are very helpful when troubleshooting the point-in-time performance issues – such as issues occurring during a specific timeframe,&#160; say between 2:00 PM and 4:00 PM. Standard Reports and Activity Monitor in Management Studio are based on DMVs (Dynamic Management Views), which stores cumulative statistics since the last start of SQL Server. So if your SQL Server instance has been running since last two months and say you have been experiencing performance issues between 2:00 PM and 4:00 PM since only a last few days, then you may not get a lot of useful information from the Standard reports or Activity Monitor. Standard reports will show you the cumulative information since the last restart of SQL Server instance and not for the specific timeframe that your are interested in - such as between 2:00 PM and 4:00 PM. Similarly, Activity Monitor will display the live performance statistics from a SQL server instance and not for a specific time frame in the past. But if you want to be able to collect the diagnostic data for a specific time frame so that you can analyze it later, or so that you can share it with someone else for later troubleshooting, then SQLDiag/SQLNexus/PAL are your friends. </p>  <p><strong>How are these tools different from the new Management Data Warehouse (MDW) feature introduced in SQL Server 2008?</strong></p>  <p align="justify">The <a href="https://msdn.microsoft.com/en-us/library/dd939169.aspx" target="_blank">Management Data Warehouse (MDW)</a> is a new feature that was introduced in SQL Server 2008. MDW implements the best of SQLDiag/SQLNexus and “generally” replaces the need for each of them. It’s a framework that allows for collecting performance data from one or more SQL Server instances on an ongoing basis and storing that data permanently in a SQL Server database. MDW provides a set of inbuilt reports that enables you to go back in time, select a particular time frame and analyze performance data only for the selected timeframe. It provides similar reports as the SQLNexus tool. Customers must have enabled this feature on their SQL Server instances to be able to use it. Many customers are not fully aware of this great feature and many have not implemented it in their environment yet, so I still rely on the SQLDiag, SQLNexus and the PAL tools to assist my customers when troubleshooting performance issue. In addition, when there is a need to do in-depth analysis, I like several specific aspects of the SQLNexus reports that are not available in the MDW reports . For example, the SQLNexus reports can tell me which specific databases and logins are responsible for most CPU usage on my server. I like the Interesting Events reports available in SQLNexus while we don’t have a similar report in MDW. Another great feature I like in the SQLNexus reports is the distribution of query statistics such as # of executions, Duration, I/O, and CPU across smaller intervals. Consider a scenario where you are troubleshooting CPU spikes that occur everyday on your server between 2:00 PM and 4:00 PM. Say you collected the SQLDiag data between 2:00 PM and 4:00 PM and analyzed it with SQLNexus. Assuming that a particular SQL query was responsible for these CPU spikes, you can drill down into the SQLNexus reports and find out which particular query execution relates to the CPU spikes on the server and if it is the number of executions of that query that is responsible for the CPU spikes. </p>  <p align="justify">Don’t get me wrong, I am not trying to devalue the significance of the MDW reports here when comparing them with the SQLNexus reports. There is so much information in the MDW reports which is absent in the SQLNexus reports and for all practical purposes, MDW is a great feature to analyze generic performance issues in SQL Server. I like to consider the SQLNexus reports as a complement to the MDW reports if I need to dive deeper into the performance issues. I would suggest that you explore the reports presented by SQLNexus and MDW and see for yourself which one of them suits your taste! I love both of them :)</p>  <p align="justify">So now that we have talked about the significance of these tools, lets talk about how to use them.</p>  <p><strong>How to use SQLDiag?</strong></p>  <p align="justify">On a SQL Server 2005 server, the SQLDiag utility is present under the following folder as <font color="#8080ff">SQLDiag.exe</font> file – <em>[systemroot]\Program Files\Microsoft SQL Server\90\Tools\Binn</em>. On a SQL Server 2008 box, SQLDiag is present under <em>[systemroot]\Program Files\Microsoft SQL Server\100\Tools\Binn</em>. SQLDiag uses an XML configuration file which can be used to specify the data that you want to collect. By default, SQLDiag uses the <font color="#8080ff">SQLDiag.xml</font> configuration file that gets automatically created in the <em>\Binn</em> directory when you launch SQLDiag for the very first time. This file is configured to collect only the following information -</p>  <ul>   <li>     <div align="justify">SQL Server error logs and configuration information in a file named <font color="#8080ff">[servername]_SQL200[x]_sp_sqldiag_Shutdown.out</font></div>   </li>    <li>     <div align="justify"><a href="https://blogs.msdn.com/b/cindygross/archive/2009/12/16/sql-server-s-default-trace.aspx" target="_blank">Default trace</a> files named <font color="#8080ff">Log_[x].trc</font> (Note that these are not regular profiler traces that you want to collect for performance troubleshooting)</div>   </li>    <li>     <div align="justify">SQLDumper logs (if any)</div>   </li>    <li>     <div align="justify">System information in a file named <font color="#8080ff">[servername]_MSINFO32.txt</font></div>   </li> </ul>  <p align="justify">You can create you own XML configuration file and configure it to specify the data that you want to collect. You can make a copy of the <font color="#8080ff">SQLDiag.xml</font> file and modify it to collect Windows event logs, Perfmon counters, Profiler traces, SQL Blocking information etc. For example, to collect the event logs, enable the EventlogCollector by modifying the tag <em>&lt;EventlogCollector enabled=&quot;<strong>false</strong>&quot; startup=&quot;false&quot; shutdown=&quot;true&quot; &gt;</em> as <em>&lt;EventlogCollector enabled=&quot;<strong>true</strong>&quot; startup=&quot;false&quot; shutdown=&quot;true&quot; &gt;</em>. Similarly, you can enable the <em>&lt;PerfmonCollector&gt;</em> to collect specific performance counters and the <em>&lt;ProfilerCollector&gt;</em> to collect profiler traces. Please take a note of few important things when creating your own configuration file - </p>  <ul>   <li>     <div align="justify">Do not modify the default <font color="#8080ff">SQLDiag.xml</font> file, because this file gets overwritten every time you run the SQLDiag utility. Instead, copy this file and give it a different name, something like MyCustom_SQLDiag_config.xml</div>   </li>    <li>     <div align="justify">If you have multiple instances of SQL server running on the same box,&#160; SQLDiag (by default) will collect data from all running SQL Server instances of the same version. So if you launch SQLDiag from the <em>\90\Tools\Binn</em> directory, it will capture data from all running SQL Server 2005 instances on that server. However, you can modify your custom configuration file to specify the name of a specific SQL Server instance for data collection. All you need to do is to replace the tag <em>&lt;Instance name=&quot;*&quot;</em> with <em>&lt;Instance name=&quot;[InstanceName]&quot;</em> in your custom configuration file. If you want to collect data from only the default instance of SQL Server, modify this tag as <em>&lt;Instance name=&quot;MSSQLSERVER&quot;.</em></div>   </li>    <li>     <div align="justify">If you are running the utility on the active node of a cluster, replace the <em>&lt;Machine name=&quot;.&quot;&gt;</em> tag with <em>&lt;Machine name=&quot;[SQLServerVirtualServerName]&quot;&gt;</em></div>   </li>    <li>     <div align="justify">The <em>&lt;EventlogCollector&gt;</em> tag allows data collection at two intervals – at startup when you start the SQLDiag data collection or at shutdown when you stop the data collection. You may chose to enable either or both of them, but for most scenarios you may only want to enable the data collection at shutdown (which is the default)</div>   </li>    <li>     <div align="justify">If you do not wish to collect default traces or SQLDumper logs or system information using Msinfo32 utility, you can disable the appropriate data collectors under the <em>&lt;CustomDiagnostics&gt;</em> tag. Additionally, you can add your own custom data collector under this section.</div>   </li>    <li>     <div align="justify">The XML configuration file is case-sensitive, so be careful while enabling or disabling specific data collectors. Make sure that when you are modifying the “true” or “false” values, they are in lower case.</div>   </li>    <li>     <div align="justify">And, don’t forget to save your configuration file after making changes to it!</div>   </li> </ul>  <p align="justify">Once your custom configuration file is ready, you can launch the SQLDiag tool from the command prompt and use the /I parameter to specify the name of your configuration file, something like this -</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/5100.Capture_3F97F849.jpg" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/5100.Capture_5F00_3F97F849.jpg"><img style="margin: 0px 0px 0px 40px; display: inline" title="Capture" alt="Capture" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/7651.Capture_thumb_5D29D348.jpg" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/7651.Capture_5F00_thumb_5F00_5D29D348.jpg" width="640" height="324" /></a></p>  <p align="justify">By default, SQLDiag creates a folder with the name SQLDIAG under the <em>\Tools\binn</em> directory and dumps all the output data files into this folder. However, you can change this default location by using the /O parameter</p>  <p><strong>How to collect SQLDiag data needed to generate the SQLNexus reports</strong></p>  <p align="justify">As you may have noticed, some initial efforts are required, such as configuring your own custom configuration file, to capture meaningful performance data with the SQLDiag utility.&#160; If your goal of capturing the SQLDiag data is only to generate the SQLNexus reports, there is an easier way to do it. Follow these steps to collect the SQLDiag data, which is required to generate the SQLNexus reports - </p>  <ul>   <li>     <div align="justify">Click <a href="https://sqlnexus.codeplex.com/Wiki/Info.aspx?title=Sql2005PerfStatsScript&amp;action=Info" target="_blank">here</a> to download the appropriate scripts files to run SQLDiag. There are two .zip files available for download on this page,<font color="#4f81bd"> </font><font color="#8080ff">PerfStatsSCript2005.zip</font> for SQL Server 2005 and <font color="#8080ff">PerfStatsScript2008.zip</font> for SQL Server 2008. </div>   </li>    <li>     <div align="justify">Extract the contents of the ZIP file on a drive with plenty of disk space available on the concerned SQL Server box. I would suggest that the drive where you plan to collect the diagnostic data should have at least a couple of GBs of disk space available, since the SQL trace files may grow pretty huge depending on the load on your server. After you have started the data collection, you should monitor the size of the trace files to see how fast they are growing and if you have sufficient disk space available on the drive or not.</div>   </li>    <li>     <div align="justify">Examine the extracted files. You will notice that there are three XML configuration files and the corresponding CMD batch files. For initial troubleshooting purposes, you should use the <font color="#8080ff">SQLDiagPerfStats_Trace200[x].xml</font> file and the corresponding <font color="#8080ff">StartSQLDiagTrace2008.cmd</font> file. As the names suggest, the other two configuration files are meant for trace replay purposes (if you want to simulate production workload in your test system) and if you are interested in capturing detailed traces (statement level events). Notice that capturing detailed traces can introduce significant overheads on the server.</div>   </li>    <li>     <div align="justify">The XML configuration files are configured to collect the data from all running instances of SQL Server on the box. If you have multiple SQL Server instances running on the same computer and want to collect data from a specific instance, open the file <font color="#8080ff">SQLDiagPerfStats_Trace200[x].xml</font> in Notepad and replace “*&quot; in the tag <em>&lt;Instance name=&quot;*&quot;</em> with the actual name of the named instance (portion after the backslash) . If it’s a default instance of SQL server, replace “*” with the word “MSSQLSERVER”. Click on File -&gt; Save to save the file.</div>   </li>    <li>     <div align="justify">When you are ready to start the SQLDiag data collection, open command prompt (use Run as Administrator options if on Windows Server 2008) and navigate to the folder where you had extracted the perf stats script files. Run the file named <font color="#8080ff">StartSQLDiagTrace200[x].cmd</font>. This will start the SQLDiag data collection processes and you should see a prompt saying ‘Data collection has started, press CTRL + C to stop’</div>   </li>    <li>     <div align="justify">The SQLDiag tool will create a folder with the name SQLDiagOutput inside the same folder where you are running this script from. All output data files are captured inside this folder</div>   </li>    <li>     <div align="justify">Allow the tool to run for the duration of the performance problems, while SQLDiag captures the diagnostic data. When you are ready to stop the SQLDiag data collection, press CTRL + C.&#160; Do not close the command prompt window while SQLDiag is in the process of shutting down. Doing so can leave orphaned processes on your server. Close the command window only after it returns back the the prompt.</div>   </li>    <li>     <div align="justify">Examine the files inside the SQLDiagOutput folder. You should see one (or more) Perfmon counter files with extension .BLG, one (or more ) trace files with extension .TRC, three .CSV files one for each Windows event logs and a bunch of files with extension .OUT that contains SQL server error logs, configuration, blocking and wait stats data.</div>   </li> </ul>  <p align="justify">Now that you have captured the SQLDiag data, you are ready to analyze it using the SQLNexus tool.</p>  <p><strong>How to use the SQLNexus tool at analyze SQLDiag data?</strong></p>  <p align="justify">The SQLNexus tool requires a SQL Server 2005 or 2008 databases for its analysis. Install a local SQL Server 2005/2008 instance on the machine where you plan to do the SQLNexus analysis, if you don’t have one already. A remote SQL server instance will work okay as well, but I would recommend using a local instance if possible. I would not recommend using the SQLNexus tool on a production server or using the production SQL Server instance for analysis, even if it is possible in your environment. Also note that prior to using SQLNexus, you would need to install the pre-requisite components as discussed in the steps below.</p>  <p align="justify">Follow these steps to use the SQLNexus tool for analyzing SQLDiag data -</p>  <ul>   <li>     <div align="justify">Download and install the <a href="https://www.microsoft.com/downloads/en/details.aspx?familyid=BB196D5D-76C2-4A0E-9458-267D22B6AAC6&amp;displaylang=en" target="_blank">Report Viewer 2008 Redistributable</a>. This is a pre-requisite for SQLNexus</div>   </li>    <li>     <div align="justify">Download and install the <a href="https://www.microsoft.com/downloads/en/details.aspx?FamilyId=7EDFA95A-A32F-440F-A3A8-5160C8DBE926&amp;amp;displaylang=en" target="_blank">32 bit version of RML utilities</a> and here to install <a href="https://www.microsoft.com/downloads/en/details.aspx?familyid=B60CDFA3-732E-4347-9C06-2D1F1F84C342&amp;amp;displaylang=en" target="_blank">64 bit version of RML utilities</a>. This is a pre-requisite for SQLNexus</div>   </li>    <li>     <div align="justify">Download the <font color="#8080ff">SQLNexus.zip</font> file from <a href="https://sqlnexus.codeplex.com/" target="_blank">here</a></div>   </li>    <li>     <div align="justify">Extract the contents of the zipped file in a folder. SQLNexus doesn’t require any installation as such. All it needs to run are the files inside the extracted folder. Since SQLNexus does not get installed like a regular Windows application, you won’t see a shortcut for it under Start –&gt; Programs or on the Desktop. I would suggest creating a shortcut for the <font color="#8080ff">SQLNexus.exe</font> in the shortcut pane or on the desktop</div>   </li>    <li>     <div align="justify">Launch the <font color="#8080ff">SQLNexus.exe</font> from the location where you extracted the contents. You will see the following screen that prompts for the SQL server name and the instance name. Type in the instance name that you plan to use for the SQLNexus analysis. </div>   </li> </ul>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/3817.image_653AFCD8.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/3817.image_5F00_653AFCD8.png"><img style="margin: 0px 0px 0px 40px; display: inline" title="image" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/7853.image_thumb_1BC8A81D.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/7853.image_5F00_thumb_5F00_1BC8A81D.png" width="240" height="157" /></a></p>  <ul>   <li>     <div align="justify">Once you click on Connect on the above screen, SQLNexus will create a database by the name SQLNexus on the instance that you have specified. If you want to use SQLNexus again to analyze a different set of SQLDiag data, either you would need to delete this database manually, or create a new database from within SQLNexus, which can be done by using the Current DB box on the toolbar and selecting the option &lt;New Database&gt;.</div>   </li>    <li>     <div align="justify">Click on the <font color="#8080ff">Import</font> option under Data menu and browse to the SQLDiagOutput folder that contains the SQLDiag data. Click on the Import button.</div>   </li> </ul>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/1665.Capture_199721D6.jpg" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/1665.Capture_5F00_199721D6.jpg"><img style="margin: 0px 0px 0px 40px; display: inline" title="Capture" alt="Capture" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/1754.Capture_thumb_215F1478.jpg" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/1754.Capture_5F00_thumb_5F00_215F1478.jpg" width="600" height="428" /></a></p>  <ul>   <li>     <div align="justify">After SQLNexus has completed importing data into the SQL database, you can analyze various reports by using various links under the <font color="#8080ff">Reports</font> menu on the top left hand side. These reports are drill down reports and can be used to dive deeper into a particular area. For example to view the top queries by CPU, Duration, Reads and Writes, click on the ReadTrace_Main Report on the left hand side and then click on Unique_Batch_TopN report link. You can also export these reports to Excel, PDF or HTML format using the Export link under Tasks menu on the left hand side.</div>   </li> </ul>  <p align="justify"><strong>Note:</strong> On several occasions, I had noticed that SQLNexus would fail to analyze the trace files. I have been able to resolve that issue by moving the _Blk.trc file to separate location or deleting it altogether. This trace file captures the blocking chains from the data collection period. SQLNexus uses a different set of scripts based on DMVs for capturing blocking and wait stats information and so this file is not required by the SQLNexus tool. </p>  <p><strong>How to use the PAL tool to analyze performance counters data (.BLG files) collected by SQLDiag?</strong></p>  <p align="justify">The PAL (Performance Analysis of Logs) tool is a powerful tool that reads in a performance monitor counter log and analyzes it using known thresholds. Follow these steps to use the PAL tool to analyze performance counters collected by the SQLDiag tool in one or more .BLG files -</p>  <ul>   <li>     <div align="justify">If you are using older Windows versions such as Windows XP/Vista or Windows Server 2003, download and install the following pre-requisites - <a href="https://download.microsoft.com/download/2/0/e/20e90413-712f-438c-988e-fdaa79a8ac3d/dotnetfx35.exe" target="_blank">Microsoft .NET Framework 3.5 Service Pack 1</a> and <a href="https://support.microsoft.com/kb/968929" target="_blank">PowerShell v2.0</a> or greater</div>   </li>    <li>     <div align="justify">Download and install the Microsoft Chart Controls for Microsoft .NET Framework 3.5 from <a href="https://www.microsoft.com/downloads/en/details.aspx?FamilyId=130F7986-BF49-4FE5-9CA8-910AE6EA442C&amp;displaylang=en" target="_blank">here</a>, which is another pre-requisite for the PAL tool</div>   </li>    <li>     <div align="justify">Download and install the PAL tool from <a href="https://pal.codeplex.com/" target="_blank">here</a></div>   </li>    <li>     <div align="justify">Launch PAL by clicking on Start –&gt; Programs –&gt; PAL. Click Next on the Welcome screen</div>   </li>    <li>     <div align="justify">On the Counter Log page, browse and select the <font color="#8080ff">SQLDiag.blg</font> file in the SQLDiagOutput folder, and click Next</div>   </li> </ul>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/7041.image_2C8CF6D6.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/7041.image_5F00_2C8CF6D6.png"><img style="margin: 0px 0px 0px 40px; display: inline" title="image" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/4721.image_thumb_5BFB65A2.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/4721.image_5F00_thumb_5F00_5BFB65A2.png" width="480" height="378" /></a></p>  <ul>   <li>On the Threshold file page, select the option “Microsoft SQL Server 2005/2008” from the drop down box and click on Next </li> </ul>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/4237.image_4FF9756E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/4237.image_5F00_4FF9756E.png"><img style="margin: 0px 0px 0px 40px; display: inline" title="image" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/7776.image_thumb_79F97396.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/7776.image_5F00_thumb_5F00_79F97396.png" width="480" height="378" /></a></p>  <ul>   <li>     <div align="justify">Answer all questions on the Questions page and click on Next</div>   </li>    <li>     <div align="justify">Select appropriate options on the Output Options, File Output, Queue and Execute pages. For most cases, you can leave these options at their default settings.</div>   </li>    <li>     <div align="justify">After you click on Finish, a command prompt window will open automatically and close once the PAL the analysis is completed.</div>   </li> </ul>  <p align="justify">PAL analysis may take some time depending on the amount of Perfmon data that you have collected, so please be patient. Once the analysis is complete, PAL reports will open up automatically in a browser window.&#160; By default, these reports are stored under My Documents\PAL Reports.</p>  <p align="justify">Here are some additional reference links if you are interested in learning more about the PAL tool -</p>  <ul>   <li>     <div align="justify"><a href="https://www.codeplex.com/Release/ProjectReleases.aspx?ProjectName=PAL&amp;ReleaseId=6759" target="_blank">Training on PAL tool</a></div>   </li>    <li>     <div align="justify"><a href="https://blogs.technet.com/clint_huffman" target="_blank">Clint Huffman's Windows Performance Analysis Blog</a></div>   </li> </ul>  <p align="justify">I hope you would enjoy working with these tools and the wonderful reports that they present! Please feel free to leave me any comments or feedback you may have and I will get back to you as soon as possible</p>  <p>Cheers!</p>]]></content:encoded>
    </item>
    <item>
      <title>Notes - SQL Server Index Fragmentation, Types and Solutions</title>
      <link>https://docs.microsoft.com/archive/blogs/pamitt/notes-sql-server-index-fragmentation-types-and-solutions</link>
      <pubDate>Thu, 23 Dec 2010 17:24:22 GMT</pubDate>
      <dc:creator><![CDATA[Pankaj Mittal]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/pamitt/2010/12/23/notes-sql-server-index-fragmentation-types-and-solutions/</guid>
      <description><![CDATA[This is the first in the series of blog posts that I plan on writing to summarize various topics of...]]></description>
      <content:encoded><![CDATA[<p align="justify">This is the first in the series of blog posts that I plan on writing to summarize various topics of interest in SQL Server. The goal of these “Notes” posts is to collect important information from multiple sources such as whitepapers, KB articles, MSDN/TechNet articles, credible blogs etc., and publish them in a single post for easy access. These summary posts should provide a quick reference point to those of us who are just looking for a brief summary on specific topics of interest in SQL Server. At the end of each summary article, I will attempt to provide links to all the resources, which I referred to while writing the post.</p>  <p align="justify">So I decided to write my first notes post on an important and sometimes perplexing topic - <strong>Index</strong> <strong>Fragmentation in SQL Server</strong>. I have often found it challenging to memorize types of index fragmentation, their causes and various differences between Reorganizing vs. Rebuilding indexes. In this post I will attempt to provide notes on these aspects of index fragmentation</p>  <p align="justify"><strong><font size="2">What is Index Fragmentation:</font></strong> Index fragmentation is an “expected” and “unavoidable” characteristic of any OLTP environment. Fragmentation is defined as any condition which causes more than the optimal amount of disk I/O to be performed in accessing a table, or causes the disk I/Os that are performed to take longer than they optimally would</p>  <p align="justify"><strong><font size="2">Causes of Index Fragmentation:</font></strong>&#160; Some of the most common causes of index fragmentation are - </p>  <ol>   <li>     <div align="justify"><strong>Insert and Update operations causing Page splits -</strong> One of the most common causes of index fragmentation is the “page-split” operation. A page-split occurs when there is no free space on a data page to accommodate new inserts or updates. As a result, SQL Server will moves ~ 50% data from the current page to the newly allocated data page. This is done to keep the index tree balanced so that SQL Server has to traverse through a predictable number of levels to get to any data page in the tree. This is the reason why SQL server indexes are implemented as b-trees where “b” means “balanced”. Page splits are caused by -</div>   </li> </ol>  <ul>   <ul>     <li>       <div align="justify">Non Sequential inserts – when doing a non-sequential insert, SQL Server moves ~50% of data from the old page to the newly allocated page. This would result in a page split, with each page having ~50% of data from the old page. </div>     </li>      <li>       <div align="justify">Updates to an existing row value with a larger value, which doesn’t fit on the same page</div>     </li>      <li>       <div align="justify">GUIDs if included in the clustered index key causes index fragmentation in both clustered and non-clustered indexes – see <a href="https://www.sqlskills.com/BLOGS/PAUL/post/Can-GUID-cluster-keys-cause-non-clustered-index-fragmentation.aspx" target="_blank">this</a> article for more details</div>     </li>   </ul> </ul>  <ol>   <li>     <div align="justify"><strong>Delete operations -</strong> Random delete operations, which leave individual pages in use but not completely full of rows will result in Internal Fragmentation. Similarly heavy deletes will cause pages removed to be removed from the page chain resulting in Logical Fragmentation.</div>   </li>    <li>     <div align="justify"><strong>Initial allocation of pages from mixed extents –</strong> this is well demonstrated in <a href="https://blogs.msdn.com/b/sqlserverstorageengine/archive/2008/03/01/example-data-fragmentation-with-insert-updates-measuring-it-and-fixing-it.aspx" target="_blank">this blog</a> post</div>   </li>    <li>     <div align="justify"><strong>Large row size -</strong> for example, if rows are 3200 bytes each, then only two rows can fit on a 8K page, leaving approximately 1600 bytes unused on every page</div>   </li> </ol>  <p align="justify"><strong><font size="2">Various Types of Index Fragmentation:</font></strong></p>  <ul>   <li>     <div align="justify"><strong>Internal Fragmentation</strong> –When pages are less than fully used, the part of each page that is unused constitutes a form of fragmentation, since the table’s or index’s rows are no longer packed together as tightly as they could be. This is known as Internal Fragmentation and usually occurs due to - </div>      <ul>       <li>         <div align="justify">Random deletes resulting in empty space on data pages </div>       </li>        <li>         <div align="justify">Page-splits due to inserts or updates </div>       </li>        <li>         <div align="justify">Shrinking the row such as when updating a large value to a smaller value </div>       </li>        <li>         <div align="justify">Using a fill factor of less than 100 </div>       </li>        <li>         <div align="justify">Using large row sizes</div>       </li>     </ul>   </li> </ul>  <blockquote>   <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/6378.image_2B842139.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/6378.image_5F00_2B842139.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/0676.image_thumb_35D54599.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/0676.image_5F00_thumb_5F00_35D54599.png" width="244" height="142" /></a></p> </blockquote>  <ul>   <li>     <p align="justify"><strong>Logical Fragmentation</strong>&#160; - SQL Server uses <a href="https://blogs.msdn.com/b/sqlserverstorageengine/archive/2006/06/26/647005.aspx" target="_blank">8KB Pages</a> to store data on disk. When a clustered index is created on a table, SQL Server creates a b-tree data structure for the index and links all data pages at the leaf level of the tree in a logical order by using a doubly linked list. Logical fragmentation occurs when the pages in this doubly linked list are not contiguous in the index, meaning that indexes have pages in which the logical ordering of pages, which is based on the key value, does not match the physical ordering inside the data file. This could happen due to - </p>      <ul>       <li>         <div align="justify">Page-splits due to inserts or updates </div>       </li>        <li>         <div align="justify">Heavy deletes that can cause pages be removed from the page chain, resulting in dis-contiguous page chain </div>       </li>     </ul>   </li> </ul>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/8103.image_6A75A1D4.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/8103.image_5F00_6A75A1D4.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/3125.image_thumb_2A3F885A.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/3125.image_5F00_thumb_5F00_2A3F885A.png" width="244" height="148" /></a></p>  <ul>   <li>     <p align="justify"><strong>Extent Fragmentation</strong> - Extent fragmentation occurs when the extents of a table or index are not contiguous with the database leaving extents from one or more indexes intermingled in the file. This can occur due to: </p>      <ul>       <ul>         <li>           <div align="justify">Random deletes, which could leave some of the pages in an extent unused while the extent itself is still reserved as part of the table’s space allocation. Think of it like Internal fragmentation, but in extents instead of pages </div>         </li>          <li>           <div align="justify">Deletes on ranges of contiguous rows within the table, causing one or more entire extents to become de-allocated, thus leaving a gap between the surrounding extents of the table or index </div>         </li>          <li>           <div align="justify">Interleaving of a table’s data extents with the extents of other objects </div>         </li>       </ul>     </ul>   </li> </ul>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/2543.image_03053F25.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/2543.image_5F00_03053F25.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/3716.image_thumb_2DDDA337.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/3716.image_5F00_thumb_5F00_2DDDA337.png" width="466" height="133" /></a></p>  <p align="justify"><strong><font size="2">Why does Index Fragmentation matters:</font></strong> High fragmentation can affect performance of read-intensive disk-bound workloads. Optimal performance of SELECT queries is only possible with contiguous data pages within the database file, and if the data pages are packed as fully as possible. Internal fragmentation will cause queries to read more data pages in order to retrieve the&#160; same number of records because the pages are not fully packed. Similarly Logical fragmentation and Extent fragmentation will cause the read performance to slow down because SQL Server can’t perform <a href="https://msdn.microsoft.com/en-us/library/ms191475.aspx" target="_blank">Read-Ahead reads</a>. The SQL Server Read-Ahead manager dynamically adjusts the size of reads it performs based on the physical ordering of the underlying pages. With high Logical and Extent fragmentation, the read-ahead manager must read smaller blocks of data. A few important things to note about the effects of index fragmentation on SQL Server performance -&#160; </p>  <ul>   <li>     <div align="justify">Fragmentation only affect performance if the pages have to be read from physical disk (aka physical read). However, if the pages being read are already in buffer pool cache (aka logical read), fragmentation does not affect query performance</div>   </li>    <li>     <div align="justify">Fragmentation happens because of DML operations. So if there is very little DML activity on a table, it may not get fragmented at all.&#160; </div>   </li>    <li>     <div align="justify">Fragmentation only impacts range queries and not the singleton SELECT queries. </div>   </li>    <li>     <div align="justify">Reducing or eliminating Internal fragmentation can help you save disk space.</div>   </li> </ul>  <p align="justify"><strong><font size="2">How to detect Index Fragmentation:</font></strong> Use the <strong>sys.dm_db_index_physical_stats</strong> DMF, that replaces the deprecated DBCC SHOWCONTIG command. This DMF has three modes - </p>  <ol>   <li>     <div align="justify">DETAILED - reads all data and index pages. Be careful with using this options since it causes the entire index be read into memory and may result in IO/Memory issues</div>   </li>    <li>     <div align="justify">SAMPLED: reads 1% of the pages if more than 10,000 pages</div>   </li>    <li>     <div align="justify">LIMITED: only reads the parent level of b-tree (same as DBCC SHOWCONTIG WITH FAST). Limited option doesn't report page density, since it does not read the leaf level pages</div>   </li> </ol>  <p align="justify">Pay attention to the following columns in the output of this DMF -</p>  <ul>   <li>     <div align="justify"><strong>Avg_fragmentation_in_percent</strong> – depicts logical fragmentation</div>   </li>    <li>     <div align="justify"><strong>Avg_page_space_used_in_percent</strong> – depicts internal fragmentation</div>   </li> </ul>  <p align="justify"><strong><font size="2">Solutions to address Index Fragmentation:</font></strong> Main choices are - </p>  <ul>   <li>     <div align="justify">Recreate - CREATE INDEX WITH DROP_EXISTING – Not a good choice because of possible issues with constraints (Foreign Keys etc.)</div>   </li>    <li>     <div align="justify">Rebuild - ALTER INDEX ... REBUILD (replaces DBCC REINDEX)</div>   </li>    <li>     <div align="justify">Reorganize - ALTER INDEX ... REORGANIZE (replaces DBCC INDEXDEFRAG)</div>   </li>    <li>     <div align="justify">To reduce the extent fragmentation of a heap, create a clustered index on the table and then drop the index</div>   </li> </ul>  <p><strong><font size="2">Reorganize vs. Rebuilding indexes</font></strong></p>  <table border="2" cellspacing="0" cellpadding="2" width="598"><tbody>     <tr>       <td valign="top" width="23">         <p align="center"><strong>#</strong></p>       </td>        <td valign="top" width="269"><strong><font size="2">Characteristic</font></strong></td>        <td valign="top" width="140"><strong><font size="2">Alter Index REORGANIZE</font></strong></td>        <td valign="top" width="162"><strong><font size="2">Alter Index REBUILD</font></strong></td>     </tr>      <tr>       <td valign="top" width="23">         <p align="center">1</p>       </td>        <td valign="top" width="269">Online or Offline</td>        <td valign="top" width="140">Online</td>        <td valign="top" width="162">Offline (unless using the Online keyword)</td>     </tr>      <tr>       <td valign="top" width="23">         <p align="center">2</p>       </td>        <td valign="top" width="269">Address Internal Fragmentation</td>        <td valign="top" width="140">Yes (can only raise page density)</td>        <td valign="top" width="162">Yes</td>     </tr>      <tr>       <td valign="top" width="23">         <p align="center">3</p>       </td>        <td valign="top" width="269">Address Logical Fragmentation</td>        <td valign="top" width="140">Yes</td>        <td valign="top" width="162">Yes</td>     </tr>      <tr>       <td valign="top" width="23">         <p align="center">4</p>       </td>        <td valign="top" width="269">Transaction Atomicity</td>        <td valign="top" width="140">Small Discrete Transactions</td>        <td valign="top" width="162">Single Atomic Transaction</td>     </tr>      <tr>       <td valign="top" width="23">         <p align="center">5</p>       </td>        <td valign="top" width="269">Rebuild Statistics Automatically</td>        <td valign="top" width="140">No</td>        <td valign="top" width="162">Yes</td>     </tr>      <tr>       <td valign="top" width="23">         <p align="center">6</p>       </td>        <td valign="top" width="269">Parallel Execution in multi-processor machines</td>        <td valign="top" width="140">No</td>        <td valign="top" width="162">Yes</td>     </tr>      <tr>       <td valign="top" width="23">         <p align="center">7</p>       </td>        <td valign="top" width="269">Untangle Indexes that have become interleaved within a data file</td>        <td valign="top" width="140">No</td>        <td valign="top" width="162">Yes</td>     </tr>      <tr>       <td valign="top" width="23">         <p align="center">8</p>       </td>        <td valign="top" width="269">Transaction log space used</td>        <td valign="top" width="140">Less</td>        <td valign="top" width="162">More</td>     </tr>      <tr>       <td valign="top" width="23">         <p align="center">9</p>       </td>        <td valign="top" width="269">Additional free space required in the data file</td>        <td valign="top" width="140">No</td>        <td valign="top" width="162">Yes</td>     </tr>   </tbody></table>  <p align="justify">&#160;</p>  <p align="justify"># 1 - DBCC INDEXDEFRAG is an online operation; therefore, the table and indexes are available while the index is being defragmented. DBCC DBREINDEX is an offline operation. When DBCC INDEXDEFRAG is run on non-quiescent systems on which updates to the underlying data are taking place, DBCC INDEXDEFRAG skips locked pages as it encounters them. As a result, DBCC INDEXDEFRAG may not be able to completely eliminate fragmentation. SQL Server 2005 introduces the ability to rebuild your indexes in an online fashion so that other processes are able to access the table while the rebuild is occurring. Because you can access the indexes during the rebuild, you are not limited to only rebuilding indexes during off-peak hours. For more information on online index operations, please refer to <a href="https://msdn.microsoft.com/en-us/library/ms191261.aspx" target="_blank">this</a> article</p>  <p align="justify"># 2 - REORGANIZE attempts to raise the page-density level of pages to the original fillfactor. DBCC INDEXDEFRAG does not, however, reduce page density levels on pages that currently have a higher page density than the original fillfactor.</p>  <p align="justify"># 3 - Rebuilding indexes is usually faster then reorganizing indexes if the logical fragmentation is high. A general rule of thumb is to rebuild indexes when the average logical fragmentation is more than 30%</p>  <p align="justify"># 4 - DBCC INDEXDEFRAG can be stopped and restarted without losing any work. The entire DBCC DBREINDEX operation runs as one atomic transaction. This means if you stop DBCC DBREINDEX the entire operation is rolled back, and you must start over. However, if you stop DBCC INDEXDEFRAG it stops instantly and no work is lost, because each unit of work performed by DBCC INDEXDEFRAG occurs as a separate transaction.</p>  <p align="justify"># 5 - Note that while Rebuilding indexes will automatically rebuild all index statistics (which is equivalent to a full scan), it does not update column statistics created by Query Optimizer (statistics names starting with _WA in sys.stats view) automatically.</p>  <p align="justify"># 7 - Interleaving occurs when index extents (a group of eight index pages) for an index are not completely contiguous within the data file, leaving extents from one or more indexes intermingled in the file. Interleaving can occur even when there is no logical fragmentation, because all index pages are not necessarily contiguous, even when logical ordering matches physical ordering.</p>  <p align="justify"># 8 - When in full recovery mode, Alter Index REBUILD logs images of each index page, which does not occur when in bulk logged mode. For this reason, in full recovery mode, the log space required by the REBUILD operation is roughly equivalent to the number of index pages multiplied by 8 KB. On the other hand&#160; the amount of work that Alter Index REORGANIZE performs when defragmenting indexes is dependent on the number of page movements and the amount of page compaction necessary. You can reclaim the log space used by the REORGANIZE operation by backing up the log because the work performed is a series of small transactions</p>  <p align="justify"># 9 - Rebuilding indexes requires adequate free space in the data file(s). With not enough free space in the data file(s), re-index operation may be unable to rebuild the indexes, or the indexes may be rebuilt with logical fragmentation values above zero. Without large enough contiguous blocks of free space, rebuilding indexes may be forced to reuse other areas of free space within the data files, resulting in indexes being rebuilt with a small amount of logical fragmentation</p>  <p><strong><font size="2">References:</font></strong></p>  <ul>   <li>     <div align="justify"><a href="https://technet.microsoft.com/en-us/sqlserver/gg429790.aspx" target="_blank">Index Fragmentation – video from Paul Randal</a></div>   </li>    <li>     <div align="justify"><a href="https://msdn.microsoft.com/en-us/library/cc966523.aspx" target="_blank">Microsoft SQL Server 2000 Index Defragmentation Best Practices</a></div>   </li>    <li>     <div align="justify"><a href="https://www.whitesands.com/Products/frag_white.pdf" target="_blank">Fragmentation and Database Performance</a></div>   </li>    <li>     <div align="justify"><a href="https://blogs.msdn.com/b/sqlserverstorageengine/archive/2008/03/01/example-data-fragmentation-with-insert-updates-measuring-it-and-fixing-it.aspx" target="_blank">Example: Index fragmentation with insert/updates, measuring it and fixing it</a></div>   </li>    <li>     <div align="justify"><a href="https://www.sqlskills.com/BLOGS/PAUL/post/Can-GUID-cluster-keys-cause-non-clustered-index-fragmentation.aspx" target="_blank">Can GUID cluster keys cause non-clustered index fragmentation?</a></div>   </li>    <li>     <div align="justify"><a href="https://blogs.msdn.com/b/sqlserverstorageengine/archive/2006/06/23/644607.aspx" target="_blank">Fragmentation (part 1): What are records?</a></div>   </li>    <li>     <div align="justify"><a href="https://blogs.msdn.com/b/sqlserverstorageengine/archive/2006/06/26/647005.aspx" target="_blank">Fragmentation (part 2): What are pages?</a></div>   </li>    <li>     <div align="justify"><a href="https://blogs.msdn.com/b/sqlserverstorageengine/archive/2006/06/28/649884.aspx" target="_blank">Fragmentation (part 3): What are extents?</a></div>   </li>    <li>     <div align="justify"><a href="https://blogs.msdn.com/b/sqlserverstorageengine/archive/2006/09/19/761437.aspx" target="_blank">Fragmentation (part 4): what are heaps?</a></div>   </li>    <li>     <div align="justify"><a href="https://blogs.technet.com/b/josebda/archive/2009/03/20/sql-server-2008-fragmentation.aspx" target="_blank">SQL Server 2008 Fragmentation – Jose Barreto’s notes</a></div>   </li>    <li>     <div align="justify"><a href="https://msdn.microsoft.com/en-us/library/ms188917.aspx" target="_blank">sys.dm_db_index_physical_stats</a></div>   </li>    <li>     <div align="justify"><a href="https://msdn.microsoft.com/en-us/library/ms189858.aspx" target="_blank">Reorganizing and Rebuilding Indexes</a></div>   </li>    <li>     <div align="justify"><a href="https://msdn.microsoft.com/en-us/library/ms191261.aspx" target="_blank">How Online Index Operations Work</a></div>   </li>    <li>     <div align="justify"><a href="https://msdn.microsoft.com/en-us/library/ms191475.aspx" target="_blank">Reading Pages</a></div>   </li> </ul>  <p align="justify"></p>  <blockquote>   <p align="left"></p></blockquote>]]></content:encoded>
    </item>
    <item>
      <title>Attention events can cause open transactions and blocking in SQL Server</title>
      <link>https://docs.microsoft.com/archive/blogs/pamitt/attention-events-can-cause-open-transactions-and-blocking-in-sql-server</link>
      <pubDate>Sun, 07 Nov 2010 13:45:00 GMT</pubDate>
      <dc:creator><![CDATA[Pankaj Mittal]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/pamitt/2010/11/07/attention-events-can-cause-open-transactions-and-blocking-in-sql-server/</guid>
      <description><![CDATA[Wondering what causes Attention events in a SQL server Profiler trace and how these events can...]]></description>
      <content:encoded><![CDATA[<p align="justify"><span style="font-size: small;" size="2">Wondering what causes Attention events in a SQL server Profiler trace and how these events can result into severe blocking problems? I will attempt to provide some answers in this post.</span></p>
<p align="justify"><span style="font-size: small;" size="2">First of all let&rsquo;s talk about what Attentions events are and what may cause them. According to BOL &ndash;</span></p>
<p align="justify"><span style="font-size: small;" size="2">"The Attention event class indicates that an attention event, such as cancel, client-interrupt requests, or broken client connections, has occurred. Cancel operations can also be seen as part of implementing data access driver time-outs"</span></p>
<p align="justify"><span style="font-size: small;" size="2">There are three most common reasons of Attention events that I have seen in my experience so far. In each case, Attention events were causing open transactions and resulting in massive blocking issues. Here are those three scenarios -</span></p>
<p align="justify"><span size="3"><strong>1. Query Cancellation</strong></span></p>
<p align="justify"><span style="font-size: small;" size="2">User may cancel the currently executing query/batch request at any time - may be the user got tired of waiting on the screen! If the query or the batch was in an explicit user transaction (BEGIN TRAN &hellip;. END TRAN), these Attention events could result in open transactions and severe blocking problems. We can reproduce this scenario from within SQL Server Management Studio. Try the following - </span></p>
<ul>
<li>
<div align="justify"><span style="font-size: small;"><span size="2">Run a SQL Server Profiler trace with the following events selected - </span><span size="2">Attention, SQL:BatchCompleted, SQL:BatchStarting, SQL:StmtCompleted, SQL:StmtStarting</span></span></div>
</li>
</ul>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">Open Management Studio and run the following batch &ndash;</span>
<div style="margin: 0px; display: inline; float: none; padding: 0px;" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:178ca605-9e55-4418-ae01-f69b373fb436" class="wlWriterEditableSmartContent"></div>
</div>
</li>
</ul>
<div><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com -->
<pre style="padding-left: 30px;" class="scroll"><code class="mysql"> USE TEMPDB <br /> GO <br /> <br /> IF EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID (N'[DBO].[TEST]') AND TYPE in (N'U')) <br /> DROP TABLE [DBO].[TEST] <br /> GO <br /> <br /> --Create a Test table and Insert few records in it <br /> <br /> CREATE TABLE TEST (C1 INT, C2 VARCHAR (100)) <br /> GO <br /> <br /> INSERT INTO TEST VALUES (1, 'AAA') <br /> GO <br /> INSERT INTO TEST VALUES (2, 'BBB') <br /> GO <br /> <br /> -- Simulate a user transaction that will take a several seconds to execute <br /> -- we will use the WAITFOR command to simulate such a transaction <br /> <br /> BEGIN TRAN <br /> UPDATE TEST SET C2 = 'MODIFY1' <br /> WHERE C1 = 1 <br /> WAITFOR DELAY '00:00:45' <br /> COMMIT TRAN</code></pre>
</div>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">While the transaction is in progress, click on the Cancel option (red square button on the toolbar) -</span></div>
</li>
</ul>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">You will notice an Attention event in Profiler trace. </span></div>
</li>
</ul>
<blockquote>
<p align="justify"><span style="font-size: x-small;" size="2"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/8787.2_Profiler_Showing_Attention_39A41E07.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/8787.2_5F00_Profiler_5F00_Showing_5F00_Attention_5F00_39A41E07.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="2_Profiler_Showing_Attention" border="0" alt="2_Profiler_Showing_Attention" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/6237.2_Profiler_Showing_Attention_thumb_0F083D2A.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/6237.2_5F00_Profiler_5F00_Showing_5F00_Attention_5F00_thumb_5F00_0F083D2A.png" width="535" height="205" /></a></span></p>
</blockquote>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">In the trace, you will also notice that the COMMIT TRAN statement never executed and so the transaction is still open. You can confirm that by running the following statement from the same query window in which you had cancelled the query</span></div>
</li>
</ul>
<blockquote>
<div align="justify">
<div style="margin: 0px; padding: 0px; float: none; display: inline;" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:920db277-0b68-4af0-8905-30e4a65cc674" class="wlWriterEditableSmartContent">
<div><!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
https://www.CodeHighlighter.com/

-->
<pre class="scroll"><code class="mysql">SELECT @@TRANCOUNT <br />GO</code></pre>
</div>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com --></div>
</div>
</blockquote>
<ul>
<li>
<div align="justify"><span style="font-size: small;"><span size="2">Now let&rsquo;s see what kind of locks are being held by this open transaction. Run a select query on the <strong>sys.dm_tran_locks</strong> DMV (you may want to filter the result on your session to exclude extra noise). You should notice an exclusive (X) lock on the row (RID), and Intent exclusive (IX) locks on the Page and the table. </span><span size="2">So with those locks in place, if another user starts a query that needs a conflicting lock on any of these resources, you will see blocking. For example, if another user/transaction/query tries to SELECT/UPDATE/DELETE the same row, it will get blocked by the open transaction. Try running this SELECT query in a another query window</span></span></div>
</li>
</ul>
<blockquote>
<div align="justify">
<div style="margin: 0px; padding: 0px; float: none; display: inline;" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:a143680b-7006-47fd-8ccd-42ae3c47e2c5" class="wlWriterEditableSmartContent">
<div><!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
https://www.CodeHighlighter.com/

-->
<pre class="scroll"><code class="mysql">SELECT * FROM TEST WHERE C1=1 <br />GO </code></pre>
</div>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com --></div>
</div>
</blockquote>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">Open the &ldquo;Activity - All Blocking Transactions&rdquo; server report to analyze the blocking chain (right click on your SQL instance in Object Explorer -&gt; Reports -&gt; Standard Reports -&gt; Activity &ndash; All Blocking Transactions)</span></div>
</li>
</ul>
<blockquote>
<p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/2844.6_Blocking_Transaction_report_0E9C0A35.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/2844.6_5F00_Blocking_5F00_Transaction_5F00_report_5F00_0E9C0A35.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="6_Blocking_Transaction_report" border="0" alt="6_Blocking_Transaction_report" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/0383.6_Blocking_Transaction_report_thumb_6A0A7CB0.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/0383.6_5F00_Blocking_5F00_Transaction_5F00_report_5F00_thumb_5F00_6A0A7CB0.png" width="535" height="190" /></a></p>
</blockquote>
<p align="justify"><span style="font-size: small;" size="2">So the question is how long will this blocking go on for? Actually this blocking chain will go indefinitely until one of the following events occurs &ndash;</span></p>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">The open transaction is committed or rolled back manually, since we just saw that SQL server will not automatically commit or rollback the transaction on Attention events.</span></div>
</li>
<li>
<div align="justify"><span style="font-size: small;" size="2">The connection (or SQL Server session) that started the transaction is terminated. This will happen when the user disconnects from the application.</span></div>
</li>
<li>
<div align="justify"><span style="font-size: small;" size="2">The connection is recycled in a connection the pool, if the application is using connection pooling.</span></div>
</li>
</ul>
<p align="justify"><span size="3"><strong>2. Query Timeouts:</strong></span></p>
<p align="justify"><span style="font-size: small;" size="2">This is the second most common reason I have seen in my experience, which causes Attention events, open transactions and blocking issues. The default query timeout in SQL Server is 0, which means that a query will run indefinitely until completion. However, application developers can change the default query timeout value in SQL Server connection string. Also, OLEDB and ODBC providers of SQL Server have non-zero query timeout values as discussed in this article &ndash; <a href="https://vyaskn.tripod.com/watch_your_timeouts.htm" target="_blank">DBA&rsquo;s Quick Guide to Timeouts</a>. Query timeouts, if not handled properly, can cause blocking issues in SQL Server. Let&rsquo;s see how that can happen -</span></p>
<ul>
<li>
<div align="justify"><span style="font-size: small;"><span size="2">Open a new query editor window in Management Studio that will prompt you to specify the authentication details. You can do that by clicking on the File menu &ndash;&gt; New &ndash;&gt; Database Engine Query. On the Connect to Database Engine screen, c</span><span size="2">lick on the <strong>Option &gt;&gt;</strong> button and change the Execution time-out setting from default of 0 to 15. The default value of 0 means the query will run indefinitely. Here is a snapshot-</span></span></div>
</li>
</ul>
<blockquote>
<p align="justify"><span style="font-size: x-small;" size="2"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/6403.9_SSMS_Change_Execution_Timeout_4558354E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/6403.9_5F00_SSMS_5F00_Change_5F00_Execution_5F00_Timeout_5F00_4558354E.png"><img style="padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="9_SSMS_Change_Execution_Timeout" border="0" alt="9_SSMS_Change_Execution_Timeout" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/3582.9_SSMS_Change_Execution_Timeout_thumb_5A863AF6.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/3582.9_5F00_SSMS_5F00_Change_5F00_Execution_5F00_Timeout_5F00_thumb_5F00_5A863AF6.png" width="208" height="244" /></a></span></p>
</blockquote>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">Restart the Profiler trace and run the following batch from the query window that you just opened &ndash;</span></div>
</li>
</ul>
<pre style="padding-left: 30px;" class="scroll"><code class="mysql"> USE TEMPDB <br /> GO <br /> <br /> -- Run a TSQL batch request that would take a few seconds to execute <br /> -- We will use the WAITFOR command to simulate such a query <br /> <br /> IF EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[TEST2]') AND TYPE IN (N'U')) <br /> DROP TABLE [DBO].[TEST2] <br /> GO <br /> <br /> --Create a Test table and Insert few records in it <br /> <br /> CREATE TABLE TEST2 (C1 INT, C2 VARCHAR (100)) <br /> GO <br /> <br /> INSERT INTO TEST2 VALUES (1, 'AAA') <br /> GO <br /> INSERT INTO TEST2 VALUES (2, 'BBB') <br /> GO <br /> <br /> --Start a user transaction that would take about 10 seconds to complete<br /> <br /> BEGIN TRAN <br /> UPDATE TEST2 SET C2 = 'MODIFY1' <br /> WHERE C1 = 1 <br /> WAITFOR DELAY '00:00:45' <br /> COMMIT TRAN</code></pre>
<p><span style="font-size: small;" size="2">After 15 seconds, which is the query timeout value that we had set while connecting to the server, the batch will terminate with the following timeout error &ndash; </span></p>
<blockquote>
<div style="margin: 0px; display: inline; float: none; padding: 0px;" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:159cc1eb-9a71-4748-84de-44cdc660ae45" class="wlWriterEditableSmartContent">
<div><!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
https://www.CodeHighlighter.com/

--><span style="color: #000000;">Msg -</span><span style="color: #000000;">2</span><span style="color: #000000;">,</span><span style="color: #000000;"> Level </span><span style="color: #000000;">11</span><span style="color: #000000;">,</span><span style="color: #000000;"> State </span><span style="color: #000000;">0</span><span style="color: #000000;">,</span><span style="color: #000000;"> Line </span><span style="color: #000000;">0</span><span style="color: #000000;"> Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.</span></div>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com --></div>
</blockquote>
<ul>
<li>
<div align="justify"><span style="font-size: small;"><span size="2">You will also notice an Attention event in the trace. </span><span size="2">If you check the open transaction count by running the SELECT @@TRANCOUNT query from the same query window in which you were running the batch, it will return 1, meaning that there is one open transaction. If you run a SELECT query on the <strong>sys.dm_tran_locks</strong> DMV, you will notice the same set of locks being held by the open transaction as you saw in the first scenario. </span><span size="2">If the application is not handling the query timeout errors and rolling back open transactions, it will result in blocking.</span></span></div>
</li>
</ul>
<blockquote>
<p align="justify"><span style="font-size: small;" size="3"><strong>What about Lock timeout errors? Will they also result in Attention events?</strong></span></p>
<p align="justify"><span style="font-size: small;" size="2">The answer is NO! Lock Timeout errors do not cause Attention events and execution will proceed to the next statements in the transaction. See it for yourself! </span></p>
</blockquote>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">Run the following batch from a new query window (if you are using an existing query window that is already open, make sure to roll back all open transactions in that window) -</span>
<div style="margin: 0px; padding: 0px; float: none; display: inline;" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:d0cdbc74-dfd0-4723-b452-dd1e9e5000f5" class="wlWriterEditableSmartContent"><br />&nbsp;</div>
<pre class="scroll"><code class="html"> USE TEMPDB <br /> GO <br /> <br /> -- Create a Test table and Insert some rows in it <br /> IF EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[TEST3]') AND TYPE IN (N'U')) <br /> DROP TABLE [DBO].[TEST3] <br /> GO <br /> <br /> CREATE TABLE TEST3 (C1 INT, C2 VARCHAR (100)) <br /> GO <br /> <br /> INSERT INTO TEST3 VALUES (1, 'AAA') <br /> GO <br /> <br /> INSERT INTO TEST3 VALUES (2, 'BBB') <br /> GO <br /> <br /> -- Run a TSQL batch request that will start a <br /> -- transaction but do not COMMIT or ROLLBACK the transaction <br /> -- I am doing this to create a classic blocking <br /> -- scenario to test lock timeout errors <br /> <br /> BEGIN TRAN <br /> UPDATE TEST3 SET C2 = 'MODIFY1' <br /> WHERE C1 = 1 <br /> GO</code></pre>
&nbsp;</div>
</li>
</ul>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">Restart the Profiler trace and run the following batch in a second query editor window &ndash; </span></div>
</li>
</ul>
<blockquote>
<div style="margin: 0px; padding: 0px; float: none; display: inline;" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:0e057390-d37d-4206-861b-2904c14d0b7d" class="wlWriterEditableSmartContent">
<div><!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
https://www.CodeHighlighter.com/

--><span style="color: #000000;"><span style="color: #000000;"></span></span>
<pre class="scroll"><code class="mysql">USE Tempdb<br /> GO<br /> <br /> SET LOCK_TIMEOUT 5000 <br /> GO <br /> <br /> BEGIN TRAN <br /> UPDATE TEST3 SET C2 = 'MODIFYAGAIN' <br /> WHERE C1 = 1 <br /> COMMIT TRAN</code></pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com --></div>
</div>
</blockquote>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">Expectedly, after 5 seconds, the batch will terminate with a lock timeout error (Error 1222) &ndash; </span></div>
</li>
</ul>
<blockquote>
<div style="margin: 0px; display: inline; float: none; padding: 0px;" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:6e67179b-70ef-433e-9d6e-be401c8d470c" class="wlWriterEditableSmartContent">
<div><!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
https://www.CodeHighlighter.com/

--><span style="color: #000000;">Msg </span><span style="color: #000000;">1222</span><span style="color: #000000;">,</span><span style="color: #000000;"> Level </span><span style="color: #000000;">16</span><span style="color: #000000;">,</span><span style="color: #000000;"> State </span><span style="color: #000000;">45</span><span style="color: #000000;">,</span><span style="color: #000000;"> Line </span><span style="color: #000000;">3</span><span style="color: #000000;"> Lock request time out period exceeded. The statement has been terminated. </span></div>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com --></div>
</blockquote>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">However, you will not see an Attention event in the Profiler trace this time. You will also notice that after the lock timeout error occurred, SQL server continued with the execution of the batch and finally committed the transaction even though the UPDATE statement had failed. If you run a SELECT @@TRANCOUNT in the same query window, it will return 0, meaning there is no open transaction</span></div>
</li>
</ul>
<blockquote>
<p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/7536.12_Trace_Lock_timeout_5C571E98.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/7536.12_5F00_Trace_5F00_Lock_5F00_timeout_5F00_5C571E98.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="12_Trace_Lock_timeout" border="0" alt="12_Trace_Lock_timeout" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/7120.12_Trace_Lock_timeout_thumb_386E473E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/7120.12_5F00_Trace_5F00_Lock_5F00_timeout_5F00_thumb_5F00_386E473E.png" width="535" height="174" /></a></p>
</blockquote>
<p align="justify"></p>
<p align="justify"><strong><span size="3">3. Applications not processing the result set completely</span></strong></p>
<p align="justify"><span style="font-size: small;" size="2">I have seen this problem while working with a customer who had an ODBC application fetching data from SQL Server. The application was coded to ask for a result set in a cursor, fetch the first few records (and not all of the requested rows) and discard the rest. This problem has been described in detail in <a href="https://blogs.msdn.com/b/psssql/archive/2008/07/23/how-it-works-attention-attention-or-should-i-say-cancel-the-query-and-be-sure-to-process-your-results.aspx" target="_blank">this blog</a>. Unfortunately, I don&rsquo;t have an application to demonstrate this behavior. But if you are noticing the following characteristics with your application and seeing a large number of Attention events and blocking chains in SQL Server, this could be the case. </span></p>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">You notice a large number of Attention events in the a SQL Server profiler trace and you have confirmed that they are not caused by user cancellation or query timeouts</span></div>
</li>
<li>
<div align="justify"><span style="font-size: small;" size="2">You are seeing a pattern in the trace showing that the Attention event arrived right after the batch started, generally milliseconds afterwards and not something like a 30 second query timeout. Here is a snapshot of this pattern that I was seeing in SQL traces - </span></div>
</li>
</ul>
<blockquote>
<p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/8666.Attention_Events_6054A28C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/8666.Attention_5F00_Events_5F00_6054A28C.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="Attention_Events" border="0" alt="Attention_Events" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/3404.Attention_Events_thumb_189319A5.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/3404.Attention_5F00_Events_5F00_thumb_5F00_189319A5.png" width="535" height="288" /></a></p>
</blockquote>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">You are seeing ASYNC_NETWORK_IO waits in SQL Server, which is an indication that the application is not processing the result set completely.</span></div>
</li>
</ul>
<p align="justify"><br /><span size="3"><strong>Solutions:</strong></span></p>
<p align="justify"><span style="font-size: small;" size="2">So now that we know how Attention events can result in open transactions and blocking problems, what can we do to resolve this problem? Here are two solutions that I have successfully tried while working with customers - </span></p>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">Check after each transaction (or before starting a new transaction in the same session) to see if the transaction is complete by using the following statement:</span></div>
</li>
</ul>
<blockquote>
<p align="justify"><span style="font-size: small;" size="2">IF @@TRANCOUNT &gt; 0 ROLLBACK</span></p>
</blockquote>
<ul>
<li>
<div align="justify"><span style="font-size: small;" size="2">Use SET XACT_ABORT ON for the connection, or in any stored procedures which begin transactions and are not cleaning up following an error. In the event of a run-time error, this setting will abort any open transactions and return control to the client. Note that T-SQL statements following the statement which caused the error will not be executed. It&rsquo;s a good practice to set XACT_ABORT to on before starting any user transactions so that any batch terminating error (such as Attention events) would roll back the entire transaction. If you can&rsquo;t modify the application easily to specify XACT_ABORT, you can try the USER_OPTIONS configuration setting in SQL Server to turn on XACT_ABORT on an entire instance of SQL Server. The below snapshot shows how to turn on XACT_ABORT on an instance of SQL Server &ndash;</span></div>
</li>
</ul>
<blockquote>
<p align="justify"><span style="font-size: x-small;" size="2"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/0361.12_XACT_ABORT_3F2150C1.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/0361.12_5F00_XACT_5F00_ABORT_5F00_3F2150C1.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="12_XACT_ABORT" border="0" alt="12_XACT_ABORT" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/41/74/metablogapi/1854.12_XACT_ABORT_thumb_17E7078C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-41-74-metablogapi/1854.12_5F00_XACT_5F00_ABORT_5F00_thumb_5F00_17E7078C.png" width="405" height="399" /></a></span></p>
</blockquote>
<blockquote>
<p align="justify"><span style="font-size: small;" size="2"><strong>Important:</strong> This (or setting it at the connection level) does not guarantee desired behavior in case of an Attention event though, since application code can override the setting. The only completely reliable way would be to set XACT_ABORT to ON before every BEGIN TRAN</span></p>
</blockquote>]]></content:encoded>
    </item>
    <item>
      <title>SQL Server 2008 and R2 Cluster Best Practices</title>
      <link>https://docs.microsoft.com/archive/blogs/pamitt/sql-server-2008-and-r2-cluster-best-practices</link>
      <pubDate>Sat, 06 Nov 2010 15:27:00 GMT</pubDate>
      <dc:creator><![CDATA[Pankaj Mittal]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/pamitt/2010/11/06/sql-server-2008-and-r2-cluster-best-practices/</guid>
      <description><![CDATA[I have been working with several of my customers recently helping them install and/or review a new...]]></description>
      <content:encoded><![CDATA[<p align="justify"><span style="font-size: small;">I have been working with several of my customers recently helping them install and/or review a new SQL Server 2008/R2 cluster environment on Windows Server 2008/R2 for best practices and gotchas. The </span><a href="https://technet.microsoft.com/en-us/library/cc732035(WS.10).aspx"><span style="font-size: small;">Cluster Validation wizard</span></a><span style="font-size: small;"> in Windows Server 2008/R2 checks for many clustering best practices and reports them as warnings in the cluster validation report. Therefore, the first thing I do to look for best practices is to review the cluster validation report. In addition to that, I usually look for the following additional best practices on a SQL Server failover cluster -</span></p>
<p align="justify"><span style="color: #f79646; font-size: small;"><strong><span style="font-size: medium;">Before starting SQL Server installation</span></strong></span></p>
<p align="justify"><b><span style="font-size: small;">1. Configure MSDTC as per best practices</span></b></p>
<p align="justify"><span style="font-size: small;">It is required to configure the MSDTC as a clustered resource on a Windows 2003 Failover cluster to be able to install SQL Server 2000/2005. If you do not cluster MS DTC prior to installing SQL Server 2000/2005 on Windows Server 2003, you may notice errors when using SQL Server features that depend on MSDTC (such as some Replication functionality). SQL Server 2008 and R2 do not have any dependency on the MSDTC service anymore and so you don&rsquo;t need to configure MSDTC as a clustered resource before installing SQL Server 2008 and R2. However, if your applications require MSDTC (which you can confirm with your application vendor or your in-house development team) or if you are using SQL Server features that always require MSDTC such as Linked Servers, then MSDTC service must be configured as a clustered resource on Windows Server 2003. There are many enhancements to the </span><a href="https://technet.microsoft.com/en-us/library/cc730992(WS.10).aspx"><span style="font-size: small;">MSDTC service in Windows Server 2008/R2</span></a><span style="font-size: small;"> that you can take advantage of. To list a few of them &ndash;</span></p>
<p align="justify"><span style="font-size: small;"></span></p>
<ul><li>
<div align="justify"><span style="font-size: small;">In Windows Server 2003, you could only have one MSDTC clustered resource, which could cause Isolation and Performance issues. In Windows Server 2008/R2, you can have multiple MSDTC clustered resources on a multi-instance (Active-Active) cluster.</span></div>
</li>
<li>
<div align="justify"><span style="font-size: small;">In Windows Server 2003, we didn&rsquo;t have the ability to run MSDTC as a standalone service on a Windows Failover cluster. In Windows Server 2008/R2 you can do that.</span></div>
</li>
</ul><p align="justify"><span style="font-size: small;">If your application uses distributed transactions or Linked servers, you might encounter various error messages if the MSDTC is not configured correctly. Sometimes application dependency on the MSDTC is not very clear, and so I usually suggest my customers to configure the MSDTC service on a cluster as per best practices. The recommended configuration parameters for MSDTC service are discussed in these articles &ndash;</span></p>
<p align="justify"><span style="font-size: small;"></span></p>
<ul><li>
<div align="justify"><a href="https://technet.microsoft.com/en-us/library/cc753620(WS.10).aspx"><span style="font-size: small;">Enable Network Access Securely for MS DTC</span></a></div>
</li>
<li>
<div align="justify"><a href="https://technet.microsoft.com/en-us/library/cc753510(WS.10).aspx"><span style="font-size: small;">Enable Network DTC Access</span></a></div>
</li>
</ul><p align="justify"><b><span style="font-size: small;">2. Pre-stage MSDTC and SQL Server account objects in Active Directory prior to installing a MSDTC or SQL server cluster</span></b></p>
<p align="justify"><span style="font-size: small;">You might run into issues while configuring a cluster resource or bringing it online, if the computer object of the cluster service does not have permissions to create computer objects in the Active Directory. If you are experiencing this problem, you will also notice the </span><a href="https://technet.microsoft.com/en-us/library/cc773535(WS.10).aspx"><span style="font-size: small;">event ID 1194</span></a><span style="font-size: small;"> in the event logs. Here is the explanation - </span></p>
<p align="justify"><span style="font-size: small;">When you create a new clustered service or application, a computer object (computer account) for that clustered service or application must be created in the Active Directory domain. This computer object is created by the computer object of the cluster service itself. If the computer object of the cluster service does not have the appropriate permissions, it cannot create or update the computer object for other clustered service or application such as SQL Server, MSDTC etc. This can result in an error with event ID 1194, while configuring the cluster resource or bringing it online. </span></p>
<p align="justify"><span style="font-size: small;">Therefore it&rsquo;s important to pre-stage a computer object for the new clustered service or application in Active Directory before configuring that application (such as SQL Server and MSTDC).</span></p>
<p align="justify"><b><span style="font-size: small;">3. Benchmark Disk Performance</span></b></p>
<p align="justify"><span style="font-size: small;">It&rsquo;s no secret that the I/O system is very important to ensure good SQL Server performance. When configuring a new server for SQL Server or when adding or modifying new disks to an existing system, it is good practice to determine the capacity of the I/O subsystem prior to deploying SQL Server. Tools such as SQLIOSIM and SQLIO can be used for storage benchmark testing and capacity planning. Here are some additional references on these tools &ndash;</span></p>
<p align="justify"><span style="font-size: small;"></span></p>
<ul><li>
<div align="justify"><a href="https://support.microsoft.com/kb/231619"><span style="font-size: small;">How to use the SQLIOSim utility to simulate SQL Server activity on a disk subsystem</span></a></div>
</li>
<li>
<div align="justify"><a href="https://blogs.msdn.com/b/sqlserverstorageengine/archive/2006/10/06/sqliosim-available-for-download.aspx"><span style="font-size: small;">SQL Server Storage Engine team blog on SQLIOSIM</span></a></div>
</li>
<li>
<div align="justify"><a href="https://blogs.msdn.com/psssql/archive/2007/04/10/sqliosim-outstanding-i-o-warnings.aspx"><span style="font-size: small;">SQLIOSim - Outstanding I/O Warnings</span></a></div>
</li>
<li>
<div align="justify"><a href="https://blogs.msdn.com/psssql/archive/2008/11/12/how-it-works-sqliosim-running-average-target-duration-discarded-buffers.aspx"><span style="font-size: small;">How It Works: SQLIOSim - Running Average, Target Duration, Discarded Buffers</span></a></div>
</li>
<li>
<div align="justify"><a href="https://sqliosimparser.codeplex.com"><span style="font-size: small;">SQLIOSIMParser</span></a></div>
</li>
<li>
<div align="justify"><a href="https://blogs.msdn.com/psssql/archive/2008/04/05/sqliosim-is-not-an-i-o-performance-tuning-tool.aspx"><span style="font-size: small;">SQLIOSim is "NOT" an I/O Performance Tuning Tool</span></a></div>
</li>
<li>
<div align="justify"><a href="https://technet.microsoft.com/en-us/library/cc966412.aspx"><span style="font-size: small;">Tools used to test I/O subsystems</span></a></div>
</li>
</ul><p align="justify"><b><span style="font-size: small;">4. Use Slip Streaming or install the latest support files prior to starting the SQL Server installation</span></b></p>
<p align="justify"><span style="font-size: small;">Slipstreaming is a process that incorporates all necessary Service Packs and hotfixes into a folder that contains the complete, original installation files. It is recommended to use the slipstreaming to install SQL Server to ensure that the new SQL Server instance is fully updated and ready to go, saving significant time. Slip streaming also installs the latest setup support files on the box, which helps avoid any bugs/known issues with the SQL Server installation engine. For more information on how to slip stream SQL Server binaries, please refer to these article -</span></p>
<p align="justify"><span style="font-size: small;"></span></p>
<ul><li>
<div align="justify"><a href="https://support.microsoft.com/kb/955392"><span style="font-size: small;">How to update or slipstream an installation of SQL Server 2008</span></a></div>
</li>
<li>
<div align="justify"><a href="https://blogs.msdn.com/b/petersad/archive/2009/02/25/sql-server-2008-creating-a-merged-slisptream-drop.aspx"><span style="font-size: small;">Creating a merged (slipstreamed) drop containing SQL Server 2008 RTM + Service Pack 1</span></a></div>
</li>
</ul><p align="justify"><span style="font-size: small;">If using Slip streaming is not an immediate option (such as due to time constraints to create the merged folder), it is recommended to install the latest setup support files in order to fix the SQL Server 2008 Setup even before you run it. For example, use the SQL Server 2008 SP2 to install the SP2 setup support files and then start the SQL Server 2008 installation wizard. This will help you avoid bugs and known issues with the SQL Server installation engine. More information on this can be found in this article - </span><a href="https://blogs.msdn.com/b/psssql/archive/2008/09/30/how-to-fix-your-sql-server-2008-setup-before-you-run-setup.aspx"><span style="font-size: small;">How to fix your SQL Server 2008 Setup before you run setup</span></a></p>
<p align="justify"><b><span style="font-size: small;">5. Ensure that the account used to install SQL Server cluster has the appropriate permissions</span></b></p>
<p align="justify"><span style="font-size: small;">I was working with a customer recently, helping them in installing a SQL Server 2008 R2 cluster on Windows Server 2008 R2 and we were getting a lot of permission errors during the installation. The setup account was a member of Local Administrators group on all nodes in the cluster. After troubleshooting the errors, we discovered that in addition to local administrative permissions, the setup account must have these additional permissions on each node in the cluster, to successfully install SQL Server - </span></p>
<p align="justify"><span style="font-size: small;"></span></p>
<ul><li>
<div align="justify"><span style="font-size: small;">Backup files and directories (SeBackupPrivilege)</span></div>
</li>
<li>
<div align="justify"><span style="font-size: small;">Debug Programs (SeDebugPrivilege)</span></div>
</li>
<li>
<div align="justify"><span style="font-size: small;">Manage auditing and security log (SeSecurityPrivilege)</span></div>
</li>
</ul><p align="justify"><span style="font-size: small;">This issue is described in detail in this KB article - </span><a href="https://support.microsoft.com/default.aspx?scid=kb;en-US;2000257"><span style="font-size: small;">SQL Server 2008 installation will fail if the setup account does not have certain user rights</span></a><span style="font-size: small;">. We granted the above permissions to the logged on user account manually through the Local Security Policies administrative tool and were able to complete the installation successfully. I suggest that you verify the setup account permissions as described in the KB article prior to starting the SQL Server cluster installation, to avoid any setup failures.</span></p>
<p><span style="font-size: small;">
<p align="justify"><b><span style="font-size: small;">6. Ensure that the SQL Server account should not be a member of Local Administrators group</span></b></p>
<p align="justify"><span style="font-size: small;">It is not recommended to assign the local Administrative permissions to the SQL Server and/or SQL Server Agent service account. SQL Server and Agent services needs specific permissions on the box, which are granted to the service accounts automatically during installation or when the service account is changed through SQL Server Configuration Manager tool. For more information on specific permissions required the by SQL Server services, please refer to the following article - </span><a href="https://msdn.microsoft.com/en-us/library/ms143504.aspx"><span style="font-size: small;">Setting Up Windows Service Accounts</span></a></p>
</span></p>
<p align="justify"><span style="font-size: medium;"><span style="color: #f79646;"><strong>During SQL Server Installation</strong></span></span></p>
<p align="justify"><b><span style="font-size: small;">1. Use Service Identifiers (SIDs) instead of Domain groups</span></b></p>
<p align="justify"><span style="font-size: small;">SQL Server enables per-service SID for each of its services on Windows Server 2008 and R2 systems in SQL Server 2008 and SQL Server 2008 R2 to provide service isolation and defense in depth. The per-service SID is derived from the service name and is unique to that service. Service SID name for a default instance of SQL Server is NT Service\MSSQLSERVER and SQL Server Agent Service is NT Service\SQLSERVERAGENT<b>.</b> Service SID name for a named instance of SQL Server is NT Service\MSSQL$&lt;InstanceName&gt;. Service isolation enables access to specific objects without the need to run a high-privilege account or weaken the security protection of the object. By using an access control entry that contains a service SID, a SQL Server service can restrict access to its resources. For more information, please refer to this article - </span><a href="https://msdn.microsoft.com/en-us/library/ms143504.aspx"><span style="font-size: small;">Setting Up Windows Service Accounts</span></a></p>
<p align="justify"><b><span style="font-size: medium;"></span></b></p>
<p align="justify"><span style="font-size: medium;"><span style="color: #f79646;"><strong>After SQL Server Installation</strong></span></span></p>
<p align="justify"><b><span style="font-size: small;">1. Implement antivirus exclusion best practices:</span></b></p>
<p align="justify"><span style="font-size: small;">Besides contending for precious system resources such as CPU and memory, antivirus programs can cause issues during SQL Server startup and in some cases may also cause database corruption. This could happen during system startup, if an antivirus program gets a lock on a file (such as MDF, LDF etc.) before SQL Server does. It&rsquo;s true that antivirus programs have evolved quite a bit in the last few years and this is less of an issue these days. Nonetheless it&rsquo;s a best practice to exclude SQL Server related directories and files from Antivirus scanning, wherever possible. In addition to excluding the MDF, LDF and NDF database files, antivirus exclusion list should be configured to exclude these additional directories on a Windows Failover cluster &ndash; </span></p>
<p align="justify"><span style="font-size: small;">&middot; Quorum drive</span></p>
<p align="justify"><span style="font-size: small;">&middot; &lt;system root&gt;:\Windows\Cluster</span></p>
<p align="justify"><span style="font-size: small;">Please refer to this KB article for more information on how to configure an antivirus program that is running on a SQL Server box - </span><a href="https://support.microsoft.com/kb/309422"><span style="font-size: small;">Guidelines for choosing antivirus software to run on the computers that are running SQL Server</span></a></p>
<p align="justify"><b><span style="font-size: small;">2. Run SQL Server 2008 BPA tool to check for other best practices</span></b></p>
<p align="justify"><span style="font-size: small;">SQL Server Best Practices Analyzer (BPA) is a free downloadable tool that can be used to scan a SQL Server instance for common best practices and other stability issues that could result in server-down scenarios. This tool encapsulates a variety of checks to prevent most common SQL server issues, which are received by Microsoft&nbsp; support. The best part is that new test cases are added to the tool on a frequent basis to help our customers prevent most common SQL Server issues, which other customers are experiencing. The BPA tools is available for all versions of SQL Server. Here are the download links for each version -</span></p>
<p align="justify"><span style="font-size: small;"></span></p>
<ul><li>
<div align="justify"><a href="https://www.microsoft.com/downloads/en/details.aspx?FamilyID=b352eb1f-d3ca-44ee-893e-9e07339c1f22&amp;displaylang=en"><span style="font-size: small;">BPA for SQL Server 2000</span></a></div>
</li>
<li>
<div align="justify"><a href="https://www.microsoft.com/downloads/en/details.aspx?FamilyId=DA0531E4-E94C-4991-82FA-F0E3FBD05E63&amp;displaylang=en"><span style="font-size: small;">BPA for SQL Server 2005</span></a></div>
</li>
<li>
<div align="justify"><a href="https://www.microsoft.com/downloads/en/details.aspx?displaylang=en&amp;FamilyID=0fd439d7-4bff-4df7-a52f-9a1be8725591"><span style="font-size: small;">BPA for SQL Server 2008 and R2</span></a></div>
</li>
</ul><p align="justify"><b><span style="font-size: small;">3. Configure SQL Server memory settings appropriately on a multi instance cluster</span></b></p>
<p align="justify"><span style="font-size: small;">By default, every SQL Server installation has &ldquo;Max Server Memory&rdquo; parameter set to use all available physical memory. This could raise problems when multiple instances are present on the cluster. It is important to cap SQL server memory by configuring the &ldquo;max server memory&rdquo; configuration option for each instance on a multi instance cluster. This would ensure that each SQL Server instance will get sufficient amount of memory if both instances failover to the same node in the cluster during the planned or unplanned failover. Note that &ldquo;max server memory&rdquo; setting only specifies the Buffer Pool memory. SQL Server as a process acquires more memory than specified by &ldquo;max server memory&rdquo; option. Both internal and external components can allocate memory outside of the Buffer Pool and this additional memory consumed by SQL Server is known as Multi Page Allocator (MPA) or Mem to Leave (MTL). The Performance Monitor counter <span style="text-decoration: underline;">SQL Server:Memory Manager\Total Server Memory (MB)</span> can be used to find the Buffer Pool memory consumption of a SQL Server process. Another Performance Monitor counter, <span style="text-decoration: underline;">Process(sqlservr)\Private Bytes</span> can be used to know the total memory consumption by a SQL Server process. The difference of these two counters can be used to calculate the MPA/MTL memory consumption by a SQL Server instance. MPA/MTL memory is used by components as such threads (usually the biggest consumer on a busy production server), linked servers, COM components, extended stored procedures etc. Following formula can be used to calculate the &ldquo;max server memory&rdquo; for each instance in a multi instance cluster &ndash;</span></p>
<p align="justify"><span style="font-size: small;">SUM (max server memory for &ldquo;resident&rdquo; instance/s) + MPA for all instances &lt;= RAM &ndash;(OS memory + Applications) </span></p>
<p align="justify"><span style="font-size: small;">where &ldquo;resident&rdquo; instance/s refers to the total number of instances that could be running on a single node of the cluster keeping node failure into consideration. </span></p>
<p align="justify"><span style="font-size: small;">For example on a 2 node Active-Active cluster, &ldquo;resident&rdquo; instances would be 2 because if a single node fails, then both instances would failover to the same node. Let&rsquo;s assume that each node has 24 GB of RAM, OS needs 2 GB of dedicated memory, other applications needs 2 GB , MPA for each SQL Server is 1 GB and both instances need equal amount of memory. So how much &ldquo;max server memory‟ should you give to each instance in case of a node failure? Let&rsquo;s say max server memory for each instance is X GB. Using the formula above </span></p>
<blockquote>
<p align="justify"><span style="font-size: small;">SUM (max server memory for &ldquo;resident&rdquo; instance/s) + MPA for all instances &lt;= Total Available Memory &ndash;(OS memory + Applications)</span></p>
<p align="justify"><span style="font-size: small;">(X+ X) + 1GB * 2 &lt;= 24&nbsp; &ndash; (2GB + 2GB) </span></p>
<p align="justify"><span style="font-size: small;">X<b> </b>&lt;= 9 GB</span></p>
</blockquote>
<p align="justify"><span style="font-size: small;">i.e. &ldquo;max server memory&rdquo; for each SQL Server instance should be configured as 9GB in this scenario. For more information on SQL Server memory configuration, please refer to this article &ndash; <span style="text-decoration: underline;"><a href="https://msdn.microsoft.com/en-us/library/ms178067.aspx">Server Memory Options</a></span></span></p>
<p align="justify"><b><span style="font-size: small;">4. Consider adding a third passive node to the 2-node cluster</span></b></p>
<p align="justify"><span style="font-size: small;">As discussed above, when using the Active-Active configuration on a 2 node cluster, each instance is configured to use far lesser amount of memory than what is available on each node. For example on a 2 node cluster with 24GB of physical memory on each node, assuming that the OS and other application need 4 GB and the MPA for each SQL instance is 1 GB, only 9 GB of memory can be allocated to each instance. However, if we add a third passive node to this cluster, the number of &ldquo;resident&rdquo; instances becomes 1 instead of 2. In that case, we can allocate as much as 19 GB of memory to each instance by using the above formulae. Adding a third node would also provide an added advantage of having additional resources such as CPU, network and I/O on the third passive node to address planned/unplanned failover without causing performance degradation.</span></p>
<p align="justify"><span style="font-size: medium;">&nbsp;<span style="color: #f79646;"><strong>If you are using Mount Points</strong></span></span></p>
<p align="justify"><b><span style="font-size: small;">1. Mount Point Best Practices</span></b></p>
<p align="justify"><span style="font-size: small;">SQL Server 2005 and later versions failover clustered instances fully support mounted drives if the mounted drive is hosted by a cluster drive with a drive letter assigned. When mount points are being used together with the physical disks, each mount point must appear as a cluster resource. Additionally, each mount point must also be added as a SQL Server dependency. If only the root physical disks dependency is added and the mount points are not added, database corruption will occur on failover. Database corruption may also occur when SQL Server is restarted without failing over. For more information please refer to these articles - </span></p>
<ul><li>
<div align="justify"><a href="https://support.microsoft.com/kb/819546"><span style="font-size: small;">SQL Server support for mounted volumes</span></a></div>
</li>
<li>
<div align="justify"><a href="https://support.microsoft.com/kb/280297"><span style="font-size: small;">How to configure volume mount points on a Microsoft Cluster Server</span></a></div>
</li>
</ul><p align="justify"><span style="font-size: small;">To avoid any issues during SQL Server installation, it is recommended to verify that Mount Points are configured and working properly. To verify that mount points are working correctly, try copying/creating files on mounted volumes. Also make sure that the root volumes and the mounted volumes appear as &ldquo;Available Storage&rdquo; in the Failover Cluster Manager and rename the mounted volumes to display their logical function. </span></p>
<p align="justify"><b><span style="font-size: small;">2. While configuring the data directories in the SQL Server setup, do not use the root mount drive (Master drive) to store any data/log/backup files</span></b></p>
<p align="justify"><span style="font-size: small;">When using mount points, it is not recommended to use the root (host) volume to store files and folders. The root volume is the volume that is hosting the mount points. Try to use the root (host) volume exclusively for mount points. This greatly reduces the time that it takes to restore access to the mounted volumes if you have to run a chkdsk. This also reduces the time that it takes to restore from backup on the host volume. If you use the root (host) volume exclusively for mount points, the size of the host volume only has to be several MB. This reduces the probability that the volume is used for anything other than the mount points</span></p>]]></content:encoded>
    </item>
    <item>
      <title>Queries with UPDLOCK hints on small tables causing blocking</title>
      <link>https://docs.microsoft.com/archive/blogs/pamitt/queries-with-updlock-hints-on-small-tables-causing-blocking</link>
      <pubDate>Sat, 06 Nov 2010 00:20:00 GMT</pubDate>
      <dc:creator><![CDATA[Pankaj Mittal]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/pamitt/2010/11/06/queries-with-updlock-hints-on-small-tables-causing-blocking/</guid>
      <description><![CDATA[While working with one our ISV customer’s in Microsoft performance testing labs recently, we...]]></description>
      <content:encoded><![CDATA[<p align="justify"><span style="font-size: small">While working with one our ISV customer’s in Microsoft performance testing labs recently, we discovered an unexpected blocking issue with the SELECT queries using the UPDLOCK hints on a small table and in explicitly defined user transactions. I said ‘unexpected blocking issue’ because from our perspective, we were processing two different rows in a table, in two different transactions - something like transaction T1 picks up a row, say Row-1 and starts processing it, while another transaction, say T2 picks up another row say Row-2 for processing, but T1 is still blocking T2. We analyzed the output from the <strong>sys.dm_tran_locks</strong> DMV and found that transaction T2 was waiting on the same KEY resource as held by transaction T1.&#160; We then looked at the query plans of the concerned queries and noticed that SQL Server wasn’t selecting an expected non clustered index on the filter column of the SELECT query, and was instead doing a clustered index scan, resulting in blocking. If you are wondering why SQL Server wouldn’t select the expected index (notice that I said ‘expected’ and not ‘right’ :)), even when we have one on the filter column of the SELECT query, you will find the answer later in this blog.       <br /></span></p>  <p align="justify"><span style="font-size: small">Below are more details on this scenario and a few workable solutions –</span></p>  <p align="justify"><span style="font-size: small">&#160; <br /></span><span style="font-size: small"><strong><span style="font-size: medium">Scenario:</span></strong></span></p>  <p align="justify"><span style="font-size: small">You have a table in your database that has the following attributes –</span></p>  <ol>   <li>     <div align="justify"><span style="font-size: small">Few records in the table (anywhere from 1 record to a few thousand records) </span></div>   </li>    <li>     <div align="justify"><span style="font-size: small">You are using UPDLOCK hints with SELECT queries on this table in explicit user transactions (BEGIN TRAN… COMMIT TRAN) </span></div>   </li>    <li>     <div align="justify"><span style="font-size: small">The SELECT queries have a WHERE clause on a column in the table and this columns either does not have any index defined on it <span style="text-decoration: underline">or</span> has a non-unique, non-clustered index on it</span></div>   </li>    <li>     <div align="justify"><span style="font-size: small">You see blocking chains where multiple transactions on this table with SELECT and UPDLOCK are fighting for the same KEY resource (the same key hash value)</span></div>   </li> </ol>  <p><span style="font-size: small"><strong><span style="font-size: medium">Steps to reproduce this scenario:</span></strong></span></p>  <p><span style="font-size: x-small"></span></p>  <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:c9e25bc9-2ad7-4974-8366-566c229cd990" class="wlWriterEditableSmartContent"><pre style=" width: 850px; height: 613px;background-color:#EEEEEE;overflow: visible;"><div><!--

code highlighting produced by Actipro CodeHighlighter (freeware)
https://www.CodeHighlighter.com/

--><span style="color: #008080;">--</span><span style="color: #008080;"> Create a Test database</span><span style="color: #008080;">
</span><span style="color: #0000FF;">Create</span><span style="color: #000000;"> </span><span style="color: #0000FF;">Database</span><span style="color: #000000;"> Test
</span><span style="color: #0000FF;">Go</span><span style="color: #000000;">

</span><span style="color: #008080;">--</span><span style="color: #008080;"> Create a table that has the attributes as discussed above</span><span style="color: #008080;">
</span><span style="color: #0000FF;">Use</span><span style="color: #000000;"> Test
</span><span style="color: #0000FF;">Go</span><span style="color: #000000;">

</span><span style="color: #0000FF;">Create</span><span style="color: #000000;"> </span><span style="color: #0000FF;">Table</span><span style="color: #000000;"> Mytable (Col1 </span><span style="color: #0000FF;">int</span><span style="color: #000000;"> </span><span style="color: #808080;">not</span><span style="color: #000000;"> </span><span style="color: #0000FF;">null</span><span style="color: #000000;">, Col2 </span><span style="color: #0000FF;">varchar</span><span style="color: #000000;">(</span><span style="color: #800000; font-weight: bold;">100</span><span style="color: #000000;">) </span><span style="color: #0000FF;">null</span><span style="color: #000000;">, Col3 </span><span style="color: #0000FF;">bigint</span><span style="color: #000000;"> </span><span style="color: #0000FF;">null</span><span style="color: #000000;">)
</span><span style="color: #0000FF;">Go</span><span style="color: #000000;">

</span><span style="color: #008080;">--</span><span style="color: #008080;"> Create Indexes </span><span style="color: #008080;">
</span><span style="color: #0000FF;">Create</span><span style="color: #000000;"> </span><span style="color: #0000FF;">Unique</span><span style="color: #000000;"> </span><span style="color: #0000FF;">Clustered</span><span style="color: #000000;"> </span><span style="color: #0000FF;">Index</span><span style="color: #000000;"> IdxPrimary </span><span style="color: #0000FF;">on</span><span style="color: #000000;"> Mytable (Col1)
</span><span style="color: #0000FF;">Go</span><span style="color: #000000;">

</span><span style="color: #0000FF;">Create</span><span style="color: #000000;"> </span><span style="color: #0000FF;">Index</span><span style="color: #000000;"> #IdxNC </span><span style="color: #0000FF;">on</span><span style="color: #000000;"> Mytable(Col3)
</span><span style="color: #0000FF;">Go</span><span style="color: #000000;">

</span><span style="color: #008080;">--</span><span style="color: #008080;"> Insert a few records into this table</span><span style="color: #008080;">
</span><span style="color: #0000FF;">Insert</span><span style="color: #000000;"> </span><span style="color: #0000FF;">into</span><span style="color: #000000;"> Mytable </span><span style="color: #0000FF;">values</span><span style="color: #000000;"> (</span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">, </span><span style="color: #FF0000;">'</span><span style="color: #FF0000;">AAA</span><span style="color: #FF0000;">'</span><span style="color: #000000;">, </span><span style="color: #800000; font-weight: bold;">50</span><span style="color: #000000;">)
</span><span style="color: #0000FF;">Insert</span><span style="color: #000000;"> </span><span style="color: #0000FF;">into</span><span style="color: #000000;"> Mytable </span><span style="color: #0000FF;">values</span><span style="color: #000000;"> (</span><span style="color: #800000; font-weight: bold;">2</span><span style="color: #000000;">, </span><span style="color: #FF0000;">'</span><span style="color: #FF0000;">BBB</span><span style="color: #FF0000;">'</span><span style="color: #000000;">, </span><span style="color: #800000; font-weight: bold;">60</span><span style="color: #000000;">)
</span><span style="color: #0000FF;">Insert</span><span style="color: #000000;"> </span><span style="color: #0000FF;">into</span><span style="color: #000000;"> Mytable </span><span style="color: #0000FF;">values</span><span style="color: #000000;"> (</span><span style="color: #800000; font-weight: bold;">3</span><span style="color: #000000;">, </span><span style="color: #FF0000;">'</span><span style="color: #FF0000;">CCC</span><span style="color: #FF0000;">'</span><span style="color: #000000;">, </span><span style="color: #800000; font-weight: bold;">70</span><span style="color: #000000;">)
</span><span style="color: #0000FF;">Insert</span><span style="color: #000000;"> </span><span style="color: #0000FF;">into</span><span style="color: #000000;"> Mytable </span><span style="color: #0000FF;">values</span><span style="color: #000000;"> (</span><span style="color: #800000; font-weight: bold;">4</span><span style="color: #000000;">, </span><span style="color: #FF0000;">'</span><span style="color: #FF0000;">DDD</span><span style="color: #FF0000;">'</span><span style="color: #000000;">, </span><span style="color: #800000; font-weight: bold;">80</span><span style="color: #000000;">)
</span><span style="color: #0000FF;">Insert</span><span style="color: #000000;"> </span><span style="color: #0000FF;">into</span><span style="color: #000000;"> Mytable </span><span style="color: #0000FF;">values</span><span style="color: #000000;"> (</span><span style="color: #800000; font-weight: bold;">5</span><span style="color: #000000;">, </span><span style="color: #FF0000;">'</span><span style="color: #FF0000;">EEE</span><span style="color: #FF0000;">'</span><span style="color: #000000;">, </span><span style="color: #800000; font-weight: bold;">90</span><span style="color: #000000;">)
</span><span style="color: #0000FF;">Go</span><span style="color: #000000;">

</span><span style="color: #008080;">--</span><span style="color: #008080;"> Start a transaction in a separate query window, say this comes as SPID X in SQL Server</span><span style="color: #008080;">
</span><span style="color: #0000FF;">Begin</span><span style="color: #000000;"> </span><span style="color: #0000FF;">Tran</span><span style="color: #000000;"> </span><span style="color: #0000FF;">Select</span><span style="color: #000000;"> </span><span style="color: #808080;">*</span><span style="color: #000000;"> </span><span style="color: #0000FF;">from</span><span style="color: #000000;"> dbo.Mytable </span><span style="color: #0000FF;">with</span><span style="color: #000000;"> (UPDLOCK) </span><span style="color: #0000FF;">where</span><span style="color: #000000;"> Col3 </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">50</span><span style="color: #000000;">


</span><span style="color: #008080;">--</span><span style="color: #008080;"> Start another transaction in a second query window, say this comes as SPID Y in SQL Server</span><span style="color: #008080;">
</span><span style="color: #0000FF;">Begin</span><span style="color: #000000;"> </span><span style="color: #0000FF;">Tran</span><span style="color: #000000;"> </span><span style="color: #0000FF;">Select</span><span style="color: #000000;"> </span><span style="color: #808080;">*</span><span style="color: #000000;"> </span><span style="color: #0000FF;">from</span><span style="color: #000000;"> dbo.Mytable </span><span style="color: #0000FF;">with</span><span style="color: #000000;"> (UPDLOCK) </span><span style="color: #0000FF;">where</span><span style="color: #000000;"> Col3 </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">60</span><span style="color: #000000;">

</span></!--

code></div></pre><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com --></!--></div>

<p align="justify"><span style="font-size: small">What you will notice is that SPID X is blocking SPID Y. If you look at the query plan for SPID X in SQL Server Profiler, you will notice that SQL Server has chosen to do a clustered index scan, although there is a non-clustered index on the filter column Progress – </span></p>

<blockquote>
  <p><span style="background-color: #ffff00"><span style="font-size: small">Clustered Index Scan(OBJECT:([TEST].[dbo].[Mytable].[IdxPrimary]), WHERE:([Test].[dbo].[Mytable_repro].[Col3]=(50))</span></span></p>
</blockquote>

<p align="justify"><span style="font-size: small">This clustered index scan is causing the second transaction (SPID Y) to get blocked by the first transaction (SPID X). You may be wondering that even though SQL Server has chosen the clustered index scan, The locked resource is still a KEY resource (and not the entire table), so why are we seeing blocking. Here’s what’s happening - </span></p>

<blockquote>
  <p align="justify"><span style="font-size: small">SPID X start scanning the table and it checks for every row in the table to see if it matches the criteria specified in the WHERE clause. Remember that it’s doing that because the SQL Server has chosen not to use the non-clustered index on our filter column, so it doesn’t know which rows would qualify for our filter criteria (Col3 = 50). Once SPID X finds the row (or all the rows) matching the filter criteria, it locks that row (or rows) with an Update (U) Lock. So now, when SPID Y starts scanning the clustered index, it has to wait on the key that is locked by SPID X because, it has to verify whether or not the locked row matches the criteria specified in the WHERE clause. The result is that SPID X continues to hold the update lock while the transaction is in progress, while SPID Y waits on SPID X to release the Update (U) lock on that key, so that it can continue with the scan.</span></p>
</blockquote>

<p align="justify"><span style="font-size: small">So the question is why SQL Server is doing a Clustered Index Scan instead of using the non-clustered index that exists on filter column (Col3 in our scenario)? SQL Server has a cost-based query optimization engine. The goal of SQL Server Query Optimizer is this – out of many (and not all!) possible query plans, discover a plan that has the least amount of cost associated with it, with cost being a function of CPU, Memory and IO.&#160; When Query Optimizer evaluated the query plans for our SELECT query, it found that the plan with clustered index scan has a lower cost when compared to the plan with a seek on the non-clustered index followed by the lookup on the clustered index.</span></p>

<p><strong><span style="font-size: medium">Workarounds:</span></strong></p>

<p align="justify"><span style="font-size: small">Here are a couple of workarounds to fix this issue by ensuring that the Query Optimizer will select the expected non-clustered index on the filter column. You can choose any one of these depending on your application requirements. </span></p>

<p><span style="font-size: x-small"></span>

  <ul>
    <li>
      <div align="justify"><span style="font-size: small">Make the Non Clustered Index a Covering Index and include all columns referenced in the query in the index as INLCUDED columns: </span></div>
    </li>

    <li>
      <div align="justify"><span style="font-size: small">Include the filter column in the Clustered index key</span></div>
    </li>

    <li>
      <div align="justify"><span style="font-size: small">Populate the table with dummy records so that QO can pass the threshold, where it can use the Non Clustered Index on the filter column</span></div>
    </li>

    <li>
      <div align="justify"><span style="font-size: small">Use the FORCESEEK query hint in your blocking query to force the index seek.</span></div>
    </li>

    <li>
      <div align="justify"><span style="font-size: small">Use Plan Guides with the Index hint specifying the name of the NC index or with the FORCESEEK hint </span></div>
    </li>

    <li>
      <div align="justify"><span style="font-size: small">Make the Non Clustered Index a Unique index</span></div>
    </li>
  </ul>
</p>



<p align="justify"><span style="font-size: small">In our case, we worked with the ISV vendor and their partner to enforce the FORCESEEK query hint with all of their SELECT, UPDATE and DELETE queries in their application to fix this issue.</span></p>]]></content:encoded>
    </item>
  </channel>
</rss>