Adapting SQLDiag to your needs
Often times, our team is confronted with SQL Server performance issues and in most of those times, the issues are due to locking and blocking in heavily loaded scenarios. As such, our typical approach consists in gathering a SQL Server Profiler trace while running a script which periodically polls the database for blocked SPIDs. The combined output of these tools usually allows us to pinpoint the statements accountable for those phenomena.
If you haven’t heard of such a script, you’ll find it in a Microsoft Support Knowledge Base article named How to monitor blocking in SQL Server 2005 and in SQL Server 2000 , along with the instructions on how to collect information from it.
Recently, in response to one of these such problems, I decided to give SQLDiag a chance. By default, it gathers a lot of valuable information and all one needs to do is just to start it from a command prompt by giving it a couple of arguments. One of those arguments allows you to choose a template for data gathering, in which you specify which performance counters to include, which profiler events, whether to export the Event Viewer logs or not, and so on. In summary: great stuff!!
Before getting deep into those templates, I pinged Bruno, as solving-SQL-Server-performance-issues is his middle name, and asked him if he had any finely crafted templates ready to use anywhere. His answer didn’t surprise me: “I’m kind of old-school… I get a Profiler trace and the blocking script output”. Well, if that’s exactly what we need, why mess around with performance counters, MSDiag and event viewer logs? He’s absolutely right and that is the reason why I didn’t give SQLDiag such a chance before.
Still, however, I decided to run that extra mile and have SQLDiag gather those two, while saving us the time to configure the trace, locate the looping script and issue an osql command. As you probably know, SQLDiag already allows you to gather some blocking data, but notably not as much as you’d get from sp_blocker_pss80.
Of course, there are other modern approaches you might want to consider, such as those described in:
- https://blogs.technet.com/rob/archive/2008/05/26/detecting-sql-server-2005-blocking.aspx
- https://blogs.msdn.com/psssql/archive/2007/02/21/sql-server-2005-performance-statistics-script.aspx
If you’re still following, then you’re probably old school as well as Bruno and me, and so let me move on to what to do if you’d like to use SQLDiag as your new starting point for your good old troubleshooting methodology:
First, ensure the sp_blocker_pss80 stored procedure is available in the server, according to the 271509 article;
Locate the sqldiag.exe utility. The quickest way for me was opening a command prompt and issuing the command where sqldiag
Also, according to the article above, create a checkblb.sql file in the same directory in which SQLDiag resides. Here’s my version of that file:
WHILE 1=1
BEGIN
EXEC master.dbo.sp_blocker_pss80
WAITFOR DELAY '00:00:10'
END
GOCreate a new and specially crafted template in which you include the SQL Server Profiler events you usually need, and add the custom task which starts the sp_blocker_ pss80-based blocking script data gathering. Here’s my personal template flavor (I've highlighted the specifics:
<?xml version="1.0" standalone="yes"?>
<dsConfig
xmlns:pssd="https://tempuri.org"
xmlns:xsi=https://www.w3.org/2001/XMLSchema-instance
xsi:noNamespaceSchemaLocation="SQLDiag_Schema.Xsd">
<Collection setupver="3.0.1.7" casenumber="SRX000000000000">
<Machines>
<Machine name=".">
<MachineCollectors>
<EventlogCollector enabled="false" startup="false" shutdown="true" />
<PerfmonCollector enabled="false" pollinginterval="5" maxfilesize="256">
<PerfmonCounters>
<PerfmonObject name="\MSSQL$%s:General Statistics" enabled="true">
<PerfmonCounter name="\User Connections" enabled="true" />
</PerfmonObject>
</PerfmonCounters>
</PerfmonCollector>
</MachineCollectors>
<Instances>
<Instance name="*" windowsauth="false" ssver="9" user="sa">
<Collectors>
<SqldiagCollector enabled="true" startup="false" shutdown="true" />
<BlockingCollector enabled="true" pollinginterval="5" maxfilesize="350"/>
<ProfilerCollector enabled="true" template="_GeneralPerformance90.xml" pollinginterval="5" maxfilesize="350">
<Events>
<EventType name="Errors and Warnings">
<Event id="137" name="Blocked Process Report" enabled="true" />
<Event id="22" name="ErrorLog" enabled="true" />
<Event id="21" name="EventLog" enabled="true" />
<Event id="33" name="Exception" enabled="true" />
</EventType>
<EventType name="Locks">
<Event id="148" name="Deadlock Graph" enabled="true" />
<Event id="25" name="Lock:Deadlock" enabled="true" />
<Event id="59" name="Lock:Deadlock Chain" enabled="true" />
<Event id="60" name="Lock:Escalation" enabled="true" />
<Event id="189" name="Lock:Timeout (timeout > 0)" enabled="true" />
</EventType>
<EventType name="OLEDB">
<Event id="61" name="OLEDB Errors" enabled="true" />
</EventType>
<EventType name="Sessions" enabled="true">
<Event id="17" name="ExistingConnection" enabled="true" />
</EventType>
<EventType name="Stored Procedures">
<Event id="10" name="RPC:Completed" enabled="true" />
<Event id="11" name="RPC:Starting" enabled="true" />
<Event id="43" name="SP:Completed" enabled="true" />
<Event id="42" name="SP:Starting" enabled="true" />
<Event id="45" name="SP:StmtCompleted" enabled="true" />
<Event id="44" name="SP:StmtStarting" enabled="true" />
</EventType>
<EventType name="TSQL">
<Event id="12" name="SQL:BatchCompleted" enabled="true" />
<Event id="13" name="SQL:BatchStarting" enabled="true" />
<Event id="40" name="SQL:StmtStarting" enabled="true" />
<Event id="41" name="SQL:StmtCompleted" enabled="true" />
</EventType>
<EventType name="Transactions">
<Event id="19" name="DTCTransaction" enabled="true" />
<Event id="50" name="SQL Transaction" enabled="true" />
</EventType>
</Events>
</ProfilerCollector>
<CustomDiagnostics>
<CustomGroup name="sp_blocker_pss80" enabled="true" />
<CustomTask enabled="true"
groupname="sp_blocker_pss80"
taskname="Gather the sp_blocker_pss80 output"
type="Utility"
point="Startup"
wait="No"
cmd="osql -E -S%server_instance% -icheckblk.sql -o"%output_path%sp_blocker_pss80.out" -w2000" />
</CustomDiagnostics>
</Collectors>
</Instance>
</Instances>
</Machine>
</Machines>
</Collection>
<Analysis>
<Producers>
<Producer name="System Info Loader" assembly="BCPLoader.dll" selected="false" />
<Producer name="Blocker Loader" assembly="TextRowsetLoader.dll" selected="false" />
<Producer name="Event Log Loader" assembly="BCPLoader.dll" selected="false" />
<Producer name="SQLDiag Loader" assembly="TextRowsetLoader.dll" selected="false" />
</Producers>
<Analyzers>
<Analyzer name="SQLDiag Analyzer" assembly="BaseAnalyzer.dll" selected="true" />
<Analyzer name="Waitstats Analyzer" assembly="BaseAnalyzer.dll" selected="true" />
<Analyzer name="Corruption Root Cause Analyzer" assembly="HardwareAnalyzer.dll" selected="true" />
<Analyzer name="Blocker Analyzer" assembly="BlockerAnalyzer.dll" selected="true" />
<Analyzer name="Perfmon Analyzer" assembly="BaseAnalyzer.dll" selected="false" />
</Analyzers>
<ViewItems>
</ViewItems>
<Trends>
</Trends>
</Analysis>
</dsConfig>Run SQLDiag making sure you’re giving it the right template with the /I parameter.
Finally, go figure out whether you should stick with the old school method, or let SQL Server give you what you need with Dynamic Management Views and stuff alike. That’s precisely what I’ll be doing next. :-)
Oh, don’t mind the level of detail under PerfmonCollector even if it’s disabled. Take a look at the schema in SQLDiag_Schema.XSD and you’ll see why these have to be added no matter whether the whole section is disabled. The alternative, changing the schema itself, might not be a good idea as we do not know how the underlying SQLDiag code interprets the template files and that’s exactly what a schema is for: enforcing a common structure for these files, right?
Well, that’s all for now. Have fun and till next time,