Okay, I had originally intended to blog about SQL Monitor on a fairly frequent basis. Unfortunately I got rather distracted towards the end of last year by a whole host of things, both personal and work related, and I ended up only writing two posts. That was a bit feeble, so now I’m going to try make a much more concerted effort to find time to post more frequently.
In my first post I introduced the SQL Monitor data repository, and described how the monitored objects are stored in a hierarchy in the data schema, in a series of tables with a _Keys suffix. In my second post I introduced how SQL Monitor stores alerts. This was by special request from a SQL Monitor user, and was a sidetrack from my original intention to elaborate further on how monitoring data is stored, which is what this post is about.
I’ve previously described the hierarchy of the different types of monitored object, and how instances of each type of monitored object are stored in a table with a _Keys suffix. The actual monitoring data for each monitored object can be found in tables alongside the _Keys table, in similarly named tables with a _StableSamples or _UnstableSamples suffix. For example, let’s have a look at the tables used to store information about a monitored server’s logical disks:
1 2 3 4 5 6 7 8 9 |
SELECT sch.name + '.' + obj.name AS [name] FROM sys.objects obj JOIN sys.schemas sch ON sch.schema_id = obj.schema_id WHERE obj.type_desc = 'USER_TABLE' AND obj.Name LIKE 'Cluster_Machine_LogicalDisk%' AND sch.name = 'data' ORDER BY sch.name, obj.name; |
name | |
---|---|
1 | data.Cluster_Machine_LogicalDisk_Capacity_StableSamples |
2 | data.Cluster_Machine_LogicalDisk_Keys |
3 | data.Cluster_Machine_LogicalDisk_Sightings |
4 | data.Cluster_Machine_LogicalDisk_UnstableSamples |
5 | data.Cluster_Machine_LogicalDisk_Volume_StableSamples |
Let’s quickly recap how logical disk objects are stored in the _Keys tables. The entries of the data.Cluster_Machine_LogicalDisk_Keys table on my laptop are as follows:
1 2 3 4 |
SELECT [Id], [ParentId], [_Name] FROM [data].[Cluster_Machine_LogicalDisk_Keys]; |
Id | ParentId | _Name | |
---|---|---|---|
1 | 1 | 1 | HarddiskVolume1 |
2 | 2 | 1 | C: |
3 | 3 | 1 | D: |
This indicates that I have three logical disks on my laptop, C:, D: and HarddiskVolume1 (this is a hidden partition that’s not mounted, so it doesn’t get a drive letter). The ParentId references an entry in the data.Cluster_Machine_Keys table, which contains the following:
1 2 3 4 |
SELECT [Id], [ParentId], [_Name] FROM [data].[Cluster_Machine_Keys]; |
Id | ParentId | _Name | |
---|---|---|---|
1 | 1 | 2 |
This represents a machine with no name, whose ParentId in turn references an entry in data.Cluster_Keys, which contains the following:
1 2 3 |
SELECT [Id], [_Name] FROM [data].[Cluster_Keys]; |
Id | _Name | |
---|---|---|
1 | 2 | granger |
1 | 1 | granger-vm |
This shows two detected clusters, granger (my laptop) and granger-vm (a virtual machine I happen to be running). All of the above can be captured in a single query, to show all logical disks across all monitored servers.
1 2 3 4 5 6 7 8 |
SELECT cluster.[_Name] AS ClusterName, machine.[_Name] AS MachineName, logicalDisk.[_Name] AS LogicalDiskName, logicalDisk.[Id] AS LogicalDiskId FROM [data].[Cluster_Machine_LogicalDisk_Keys] logicalDisk JOIN data.Cluster_Machine_Keys machine ON logicalDisk.ParentId = machine.Id JOIN data.Cluster_Keys cluster ON machine.ParentId = cluster.Id ORDER BY cluster.[_Name], machine.[_Name], logicalDisk.[_Name]; |
ClusterName | MachineName | LogicalDiskName | LogicalDiskId | |
---|---|---|---|---|
1 | granger | C: | 2 | |
2 | granger | D: | 3 | |
3 | granger | HarddiskVolume1 | 1 |
Okay, as you can probably see, I’m not monitoring very much. I have a development version of SQL Monitor on my laptop that’s only monitoring itself. If you run this query on your own data repository, however, you’ll likely see far more entries.
So where is the actual raw monitoring data stored for these logical disks? It’s in data.Cluster_Machine_LogicalDisk_UnstableSamples. As you can probably imagine, this table can contain a large number of rows, since disk monitoring data is captured every 15 seconds, and you may have months of data for many more logical disks. Here’s what I’ve collected within the last three minutes.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
SELECT Id, CollectionDate, [_CumulativeIdleTime], [_CumulativeReadBytes], [_CumulativeReads], [_CumulativeReadTime], [_CumulativeWriteBytes], [_CumulativeWrites], [_CumulativeWriteTime], [_FreeBytes] FROM [data].[Cluster_Machine_LogicalDisk_UnstableSamples] WHERE [CollectionDate] > utils.DateTimeToTicks(DATEADD(minute, -3, GETUTCDATE())) ORDER BY [Id], [CollectionDate]; |
Id | CollectionDate | _CumulativeIdleTime | _CumulativeReadBytes | _CumulativeReads | _CumulativeReadTime | _CumulativeWriteBytes | _CumulativeWrites | _CumulativeWriteTime | _FreeBytes | |
---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 634938045375036811 | 1297384558000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 |
2 | 1 | 634938045525045391 | 1297535667000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 |
3 | 1 | 634938045675103974 | 1297686802000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 |
4 | 1 | 634938045825182558 | 1297837964000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 |
5 | 1 | 634938045975231140 | 1297989143000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 |
6 | 1 | 634938046125329726 | 1298140440000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 |
7 | 1 | 634938046275328305 | 1298291554000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 |
8 | 1 | 634938046425456892 | 1298442755000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 |
9 | 1 | 634938046575525475 | 1298593971000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 |
10 | 1 | 634938046725644062 | 1298745175000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 |
11 | 1 | 634938046875652642 | 1298896302000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 |
12 | 1 | 634938047025651221 | 1299047443000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 |
13 | 2 | 634938045375036811 | 951496085000 | 36321630720 | 588634 | 8854913000 | 25545304064 | 724638 | 1929631352000 | 21247295488 |
14 | 2 | 634938045525045391 | 951644718000 | 36321630720 | 588634 | 8854913000 | 25545756672 | 724727 | 1929633901000 | 21247295488 |
15 | 2 | 634938045675103974 | 951794748000 | 36321805312 | 588644 | 8855524000 | 25546277888 | 724785 | 1929634659000 | 21247295488 |
16 | 2 | 634938045825182558 | 951936944000 | 36322259968 | 588709 | 8860741000 | 25547313152 | 724934 | 1929640145000 | 21247295488 |
17 | 2 | 634938045975231140 | 952081195000 | 36323034112 | 588856 | 8866200000 | 25548411392 | 725078 | 1929642972000 | 21247295488 |
18 | 2 | 634938046125329726 | 952229966000 | 36323034112 | 588856 | 8866200000 | 25549129728 | 725236 | 1929645912000 | 21247295488 |
19 | 2 | 634938046275328305 | 952380355000 | 36323034112 | 588856 | 8866200000 | 25549285376 | 725272 | 1929646720000 | 21247295488 |
20 | 2 | 634938046425456892 | 952527408000 | 36323197952 | 588859 | 8869568000 | 25549637632 | 725339 | 1929647780000 | 21247295488 |
21 | 2 | 634938046575525475 | 952678458000 | 36323197952 | 588859 | 8869568000 | 25550027776 | 725370 | 1929647958000 | 21247295488 |
22 | 2 | 634938046725644062 | 952829110000 | 36323214336 | 588860 | 8869586000 | 25550510080 | 725475 | 1929648597000 | 21247295488 |
23 | 2 | 634938046875652642 | 952950997000 | 36326986752 | 589781 | 8898381000 | 25550659072 | 725504 | 1929649156000 | 21247295488 |
24 | 2 | 634938047025651221 | 953056054000 | 36332478976 | 591122 | 8942216000 | 25556641792 | 725709 | 1929655379000 | 21247295488 |
25 | 3 | 634938045375036811 | 1293765311000 | 588954624 | 121971 | 2915805000 | 43422720 | 1450 | 1757686000 | 169272672256 |
26 | 3 | 634938045525045391 | 1293916420000 | 588954624 | 121971 | 2915805000 | 43422720 | 1450 | 1757686000 | 169272672256 |
27 | 3 | 634938045675103974 | 1294067555000 | 588954624 | 121971 | 2915805000 | 43422720 | 1450 | 1757686000 | 169272672256 |
28 | 3 | 634938045825182558 | 1294218717000 | 588954624 | 121971 | 2915805000 | 43422720 | 1450 | 1757686000 | 169272672256 |
29 | 3 | 634938045975231140 | 1294330368000 | 588979200 | 121977 | 2955334000 | 43422720 | 1450 | 1757686000 | 169272672256 |
30 | 3 | 634938046125329726 | 1294481664000 | 588979200 | 121977 | 2955334000 | 43422720 | 1450 | 1757686000 | 169272672256 |
31 | 3 | 634938046275328305 | 1294632778000 | 588979200 | 121977 | 2955334000 | 43422720 | 1450 | 1757686000 | 169272672256 |
32 | 3 | 634938046425456892 | 1294783979000 | 588979200 | 121977 | 2955334000 | 43422720 | 1450 | 1757686000 | 169272672256 |
33 | 3 | 634938046575525475 | 1294935195000 | 588979200 | 121977 | 2955334000 | 43422720 | 1450 | 1757686000 | 169272672256 |
34 | 3 | 634938046725644062 | 1295086399000 | 588979200 | 121977 | 2955334000 | 43422720 | 1450 | 1757686000 | 169272672256 |
35 | 3 | 634938046875652642 | 1295237527000 | 588979200 | 121977 | 2955334000 | 43422720 | 1450 | 1757686000 | 169272672256 |
36 | 3 | 634938047025651221 | 1295388667000 | 588979200 | 121977 | 2955334000 | 43422720 | 1450 | 1757686000 | 169272672256 |
The values highlighted in red and blue are mentioned further below.
Well, what does all this mean? Let’s look at each of the columns in turn.
- Id – This is the Id of the logical disk from the data.Cluster_Machine_LogicalDisk_Keys (or LogicalDiskId in my query above).
- CollectionDate – The time at which the monitoring data was collected.
- _CumulativeIdleTime – The cumulative time that this disk has spent idling.
- _CumulativeReadBytes – The cumulative number of bytes read by this disk.
- _CumulativeReads – The cumulative number of read operations performed by this disk.
- _CumulativeReadTime – The cumulative time spent by this disk on read operations.
- _CumulativeWriteBytes – The cumulative number of bytes written by this disk.
- _CumulativeWrites – The cumulative number of write operations performed by this disk.
- _CumulativeWriteTime – The cumulative time spent by this disk on write operations.
- _FreeBytes – The number of free bytes remaining on this disk.
Hmm, there are a few questions that these results raise, which I need to cover in a little detail.
- Why aren’t the CollectionDate values datetimes?
- What are the units of the time-based properties _CumulativeIdleTime, _CumulativeReadTime and _CumulativeWriteTime?
- Most of the properties are cumulative. How do I work with them?
- Is there an easier way to tie the raw data back to the cluster, machine and logical disk?
Why aren’t the CollectionDate values datetimes?
The CollectionDate column represents the timestamp for each data collection, but the type is bigint not datetime. This is because the native datetime SQL type can only represent timestamps to a precision of approximately 3.3 milliseconds. For various reasons, including a desire to retain the original sampling accuracy of the raw data, this isn’t sufficiently precise. There is a native datetime2 SQL type which is much more precise, but this was only introduced in SQL Server 2008 and the monitoring code in SQL Monitor predates this. Therefore, to retain the original sampling precision, timestamps are stored by SQL Monitor as bigints with a precision of 100 nanoseconds (in fact, each timestamp value is represented by the number of complete 100 nanosecond intervals that have elapsed since midnight of January 1st in the year 0001 of the Gregorian calendar).
The SQL Monitor data repository includes two scalar functions that can be used to convert between the bigint timestamp representation and a datetime – utils.DateTimeToTicks and utils.TicksToDateTime. You can see an example of utils.DateTimeToTicks in action in the WHERE clause of the above query, where it’s used to limit the results to the most recent 3 minutes. For performance reasons, it is strongly recommended that you use the raw bigint values in any queries for filtering and ordering purposes. For example, in the above query, the WHERE clause
1 |
WHERE [CollectionDate] > utils.DateTimeToTicks(DATEADD(minute, -3, GETUTCDATE())) |
could have been written as
1 |
WHERE utils.TicksToDateTime([CollectionDate]) > DATEADD(minute, -3, GETUTCDATE()) |
but this would make the query significantly more expensive for SQL Server to evaluate as it can no longer take advantage of any indexes based on the CollectionDate.
What are the units of the time-based properties _CumulativeIdleTime, _CumulativeReadTime and _CumulativeWriteTime?
For the properties _CumulativeReadBytes, _CumulativeWriteBytes and _FreeBytes, it’s fairly obvious that the values represent a number of bytes. Likewise, _CumulativeReads and _CumulativeWrites represent the number of read and write operations that a disk has performed, which are simple unitless counts. So what are the units of the _CumulativeIdleTime, _CumulativeReadTime and _CumulativeWriteTime properties? Well, they represent times but their units aren’t seconds. They’re hundreds of nano-seconds (1 × 10-7 seconds). If you want to convert these values to seconds, you need to divide by 10,000,000. In fact, this is the same unit as for CollectionDate.
Most of the properties are cumulative. How do I work with them?
With the exception of _FreeBytes, all data values are cumulative. For example, if I want to know how much time my C: drive spent idling for the most recent minute, I need to look at the samples at the start and end of that minute, i.e. rows 20 and 24 in the result set above (highlighted in red and blue). The time spent idle, according to the _CumulativeIdleTime values, is given by
(953,056,054,000 – 952,527,408,000) / 10,000,000 = 52.865 seconds
The duration of that interval, according to the CollectionDate values, is given by
(634,938,047,025,651,221 – 634,938,046,425,456,892) / 10,000,000 = 60.019 seconds
The proportion of time spent idle over that minute is thus given by
52.865 / 60.019 = 0.8808 = 88%
There are a number of different strategies for performing this calculation between pairs of samples for an entire series of samples. Here’s one that involves selecting the records of interest into a temporary table, and then performing a second query against the temporary table, that joins together adjacent pairs of records.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
CREATE TABLE #tmp_Cluster_Machine_LogicalDisk_UnstableSamples ( [Row] [int] NOT NULL PRIMARY KEY, [Id] [bigint] NOT NULL, [CollectionDate] [bigint] NOT NULL, [_CumulativeIdleTime] [bigint] NULL, [_CumulativeReadBytes] [bigint] NULL, [_CumulativeReads] [bigint] NULL, [_CumulativeReadTime] [bigint] NULL, [_CumulativeWriteBytes] [bigint] NULL, [_CumulativeWrites] [bigint] NULL, [_CumulativeWriteTime] [bigint] NULL, [_FreeBytes] [bigint] NULL ); INSERT INTO #tmp_Cluster_Machine_LogicalDisk_UnstableSamples SELECT ROW_NUMBER() OVER (ORDER BY [Id], [CollectionDate]) AS Row, Id, CollectionDate, [_CumulativeIdleTime], [_CumulativeReadBytes], [_CumulativeReads], [_CumulativeReadTime], [_CumulativeWriteBytes], [_CumulativeWrites], [_CumulativeWriteTime], [_FreeBytes] FROM [data].[Cluster_Machine_LogicalDisk_UnstableSamples] WHERE [CollectionDate] > utils.DateTimeToTicks(DATEADD(minute, -3, GETUTCDATE())); SELECT b.Id AS Id, b.CollectionDate AS CollectionDate, (b.[_CumulativeIdleTime] - a.[_CumulativeIdleTime]) * 100.0 / (b.CollectionDate - a.CollectionDate) AS PercentageIdleTime, (b.[_CumulativeReadBytes] - a.[_CumulativeReadBytes]) * 10000000.0 / (b.CollectionDate - a.CollectionDate) AS ReadBytesPerSecond, (b.[_CumulativeReads] - a.[_CumulativeReads]) * 10000000.0 / (b.CollectionDate - a.CollectionDate) AS ReadsPerSecond, (b.[_CumulativeReadTime] - a.[_CumulativeReadTime]) * 100.0 / (b.CollectionDate - a.CollectionDate) AS PercentageReadTime, (b.[_CumulativeWriteBytes] - a.[_CumulativeWriteBytes]) * 10000000.0 / (b.CollectionDate - a.CollectionDate) AS WriteBytesPerSecond, (b.[_CumulativeWrites] - a.[_CumulativeWrites]) * 10000000.0 / (b.CollectionDate - a.CollectionDate) AS WritesPerSecond, (b.[_CumulativeWriteTime] - a.[_CumulativeWriteTime]) * 100.0 / (b.CollectionDate - a.CollectionDate) AS PercentageWriteTime, b.[_FreeBytes] AS FreeBytes FROM #tmp_Cluster_Machine_LogicalDisk_UnstableSamples a INNER JOIN #tmp_Cluster_Machine_LogicalDisk_UnstableSamples b ON a.Row = b.Row - 1 AND a.Id = b.Id ORDER BY [Id], [CollectionDate]; DROP TABLE #tmp_Cluster_Machine_LogicalDisk_UnstableSamples; |
Id | CollectionDate | PercentageIdleTime | ReadBytesPerSecond | ReadsPerSecond | PercentageReadTime | WriteBytesPerSecond | WritesPerSecond | PercentageWriteTime | FreeBytes | |
---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 634938933823024834 | 100.550915154319839 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 63963136 |
2 | 1 | 634938933973243426 | 100.564116590841165 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 63963136 |
3 | 1 | 634938934123001991 | 100.553848122142463 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 63963136 |
4 | 1 | 634938934273030573 | 100.556172689814531 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 63963136 |
5 | 1 | 634938934423039153 | 100.552248411390868 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 63963136 |
6 | 1 | 634938934573387752 | 100.554977569162450 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 63963136 |
7 | 1 | 634938934723406333 | 100.554877265503531 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 63963136 |
8 | 1 | 634938934873314907 | 100.554622045834416 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 63963136 |
9 | 1 | 634938935023173478 | 100.551472628148843 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 63963136 |
10 | 1 | 634938935173262063 | 100.552616976167774 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 63963136 |
11 | 1 | 634938935324140693 | 100.555658544884719 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 63963136 |
12 | 2 | 634938933823024834 | 99.376982303278919 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 411180.7471279309 | 9.7994394720 | 3.397139016981561 | 19059965952 |
13 | 2 | 634938933973243426 | 99.684065738014639 | 272.6693111329 | 0.0665696560 | 0.003994179362298 | 299084.1506489423 | 11.4499808385 | 3.102145971385486 | 19059965952 |
14 | 2 | 634938934123001991 | 100.061054938660770 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 153710.8745666733 | 10.2832181918 | 1.122473362374966 | 19059965952 |
15 | 2 | 634938934273030573 | 100.215570923679062 | 273.0146446361 | 0.0666539659 | 0.003999237958537 | 45866.4602988782 | 6.2654728017 | 0.587221440245299 | 19059965952 |
16 | 2 | 634938934423039153 | 99.039668264308614 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 47510.8823775280 | 7.0662624764 | 1.757232819616051 | 19059965952 |
17 | 2 | 634938934573387752 | 99.369066950866632 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 50672.6371291294 | 6.7842334866 | 2.115084557588727 | 19059965952 |
18 | 2 | 634938934723406333 | 99.850964461528935 | 273.0328451780 | 0.0666584094 | 0.004666088662710 | 64981.8171523699 | 7.7990339076 | 1.969089415663783 | 19059965952 |
19 | 2 | 634938934873314907 | 99.374569462584575 | 7923.7629196579 | 0.0667073252 | 0.022680490576876 | 97680.8704750937 | 10.8065866866 | 2.486849084429286 | 19059965952 |
20 | 2 | 634938935023173478 | 91.784539971357394 | 724924.5690458372 | 20.2190637464 | 78.796293873641701 | 199834.2824181874 | 17.8167987468 | 6.338643119718524 | 19059965952 |
21 | 2 | 634938935173262063 | 99.613171781185091 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 97324.9231445549 | 13.6586003525 | 2.028135584061905 | 19059965952 |
22 | 2 | 634938935324140693 | 97.350433258838577 | 734343.8895223266 | 26.8427675940 | 2.344931154266180 | 70108.8020218635 | 6.9592360429 | 1.525066869973567 | 19059965952 |
23 | 3 | 634938933823024834 | 100.514250584866545 | 42869.0145590338 | 0.2666514142 | 0.036664569453293 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 169272672256 |
24 | 3 | 634938933973243426 | 100.540817211227755 | 28357.6083578256 | 0.1331393120 | 0.022633683053027 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 169272672256 |
25 | 3 | 634938934123001991 | 100.529809430265307 | 28444.7170016619 | 0.1335482882 | 0.023370950436123 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 169272672256 |
26 | 3 | 634938934273030573 | 89.724903218774673 | 42590.2845632440 | 0.1999618979 | 10.831269471039858 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 169272672256 |
27 | 3 | 634938934423039153 | 100.518916984615146 | 42869.0145590338 | 0.2666514142 | 0.045330740414981 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 169272672256 |
28 | 3 | 634938934573387752 | 100.245696336684853 | 42499.6311405602 | 0.1995362790 | 0.308616111547537 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 169272672256 |
29 | 3 | 634938934723406333 | 100.520214892580539 | 42593.1238477718 | 0.1999752284 | 0.034662372922991 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 169272672256 |
30 | 3 | 634938934873314907 | 100.529940335500756 | 28416.2532291181 | 0.1334146504 | 0.024681710333659 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 169272672256 |
31 | 3 | 634938935023173478 | 99.148816786728868 | 28699.0591949525 | 0.2001887499 | 0.025357241662206 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 169272672256 |
32 | 3 | 634938935173262063 | 90.942292513451306 | 42573.2576531386 | 0.1998819563 | 10.985512322606012 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 169272672256 |
33 | 3 | 634938935324140693 | 100.521193756862718 | 42350.3315214354 | 0.1988353155 | 0.034464788022001 | 0.0000000000 | 0.0000000000 | 0.000000000000000 | 169272672256 |
Notice that the calculation for most of the properties involves a multiplication factor, either to convert from “per 100 nanoseconds” to “per seconds”, or to convert a bare ratio to a percentage. It’s also important that the multiplication is expressed as a float value rather than a bigint. This avoids rounding errors that otherwise would have occurred with bigint division.
There’s another calculation that’s also very useful, which is where we need to divide two cumulative changes. One classic example of this is for the Disk avg. read time and Disk avg. write time properties. For any given time interval, Disk avg. read time is calculated by dividing the total time spent reading by the number of read operations carried out. Likewise, Disk avg. write time is calculated by dividing the total time spent writing by the number of write operations carried out. These two properties can be obtained by modifying the SELECT query against the temporary table used above:
1 2 3 4 5 6 7 8 9 10 11 12 |
-- Warning! This query doesn't actually work! SELECT b.Id AS Id, b.CollectionDate AS CollectionDate, ((b.[_CumulativeReadTime] - a.[_CumulativeReadTime]) / 10000.0) / (b.[_CumulativeReads] - a.[_CumulativeReads]) AS AverageReadTimeInMilliseconds, ((b.[_CumulativeWriteTime] - a.[_CumulativeWriteTime]) / 10000.0) / (b.[_CumulativeWrites] - a.[_CumulativeWrites]) AS AverageWriteTimeInMilliseconds FROM #tmp_Cluster_Machine_LogicalDisk_UnstableSamples a INNER JOIN #tmp_Cluster_Machine_LogicalDisk_UnstableSamples b ON a.Row = b.Row - 1 AND a.Id = b.Id ORDER BY [Id], [CollectionDate]; |
Unfortunately this query fails with a “Divide by zero error encountered” error message whenever any interval is encountered in which no read or write operations occur. This can be resolved by adjusting the calculations to cope with a possible divide by zero scenario.
1 2 3 4 5 6 7 8 9 10 11 |
SELECT b.Id AS Id, b.CollectionDate AS CollectionDate, COALESCE(((b.[_CumulativeReadTime] - a.[_CumulativeReadTime]) / 10000.0) / NULLIF(b.[_CumulativeReads] - a.[_CumulativeReads], 0), 0) AS AverageReadTimeInMilliseconds, COALESCE(((b.[_CumulativeWriteTime] - a.[_CumulativeWriteTime]) / 10000.0) / NULLIF(b.[_CumulativeWrites] - a.[_CumulativeWrites], 0), 0) AS AverageWriteTimeInMilliseconds FROM #tmp_Cluster_Machine_LogicalDisk_UnstableSamples a INNER JOIN #tmp_Cluster_Machine_LogicalDisk_UnstableSamples b ON a.Row = b.Row - 1 AND a.Id = b.Id ORDER BY [Id], [CollectionDate]; |
Id | CollectionDate | AverageReadTimeInMilliseconds | AverageWriteTimeInMilliseconds | |
---|---|---|---|---|
1 | 1 | 634939030758099203 | 0.000000000000000000 | 0.000000000000000000 |
2 | 1 | 634939030908247791 | 0.000000000000000000 | 0.000000000000000000 |
3 | 1 | 634939031058316374 | 0.000000000000000000 | 0.000000000000000000 |
4 | 1 | 634939031208414959 | 0.000000000000000000 | 0.000000000000000000 |
5 | 1 | 634939031358503544 | 0.000000000000000000 | 0.000000000000000000 |
6 | 1 | 634939031508582128 | 0.000000000000000000 | 0.000000000000000000 |
7 | 1 | 634939031658620710 | 0.000000000000000000 | 0.000000000000000000 |
8 | 1 | 634939031808699294 | 0.000000000000000000 | 0.000000000000000000 |
9 | 1 | 634939031958737875 | 0.000000000000000000 | 0.000000000000000000 |
10 | 1 | 634939032108856462 | 0.000000000000000000 | 0.000000000000000000 |
11 | 1 | 634939032258845041 | 0.000000000000000000 | 0.000000000000000000 |
12 | 2 | 634939030758099203 | 0.000000000000000000 | 1.305952380952380952 |
13 | 2 | 634939030908247791 | 0.000000000000000000 | 2.992156862745098039 |
14 | 2 | 634939031058316374 | 0.000000000000000000 | 1.428846153846153846 |
15 | 2 | 634939031208414959 | 0.000000000000000000 | 2.668333333333333333 |
16 | 2 | 634939031358503544 | 0.000000000000000000 | 2.262376237623762376 |
17 | 2 | 634939031508582128 | 0.000000000000000000 | 3.438596491228070175 |
18 | 2 | 634939031658620710 | 0.600000000000000000 | 1.563440860215053763 |
19 | 2 | 634939031808699294 | 1.953403141361256544 | 1.564885496183206106 |
20 | 2 | 634939031958737875 | 43.095000000000000000 | 29.070682730923694779 |
21 | 2 | 634939032108856462 | 82.548837209302325581 | 138.539416058394160583 |
22 | 2 | 634939032258845041 | 52.330909090909090909 | 113.118010752688172043 |
23 | 3 | 634939030758099203 | 0.000000000000000000 | 0.000000000000000000 |
24 | 3 | 634939030908247791 | 0.000000000000000000 | 0.000000000000000000 |
25 | 3 | 634939031058316374 | 0.000000000000000000 | 0.000000000000000000 |
26 | 3 | 634939031208414959 | 0.000000000000000000 | 0.000000000000000000 |
27 | 3 | 634939031358503544 | 0.000000000000000000 | 0.000000000000000000 |
28 | 3 | 634939031508582128 | 0.000000000000000000 | 0.000000000000000000 |
29 | 3 | 634939031658620710 | 0.000000000000000000 | 0.000000000000000000 |
30 | 3 | 634939031808699294 | 38.593063583815028901 | 0.000000000000000000 |
31 | 3 | 634939031958737875 | 1.112078019504876219 | 0.000000000000000000 |
32 | 3 | 634939032108856462 | 0.817503010839020473 | 0.000000000000000000 |
33 | 3 | 634939032258845041 | 0.784519215928384009 | 0.000000000000000000 |
Is there an easier way to tie the raw data back to the cluster, machine and logical disk?
In the queries above, we could further join from the _UnstableSamples tables back to the different _Keys tables and also convert the bigint timestamps to datetimes, to provide more human readable output like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
SELECT cluster._Name AS ClusterName, machine._Name AS MachineName, logicalDisk._Name AS LogicalDiskName, utils.TicksToDateTime(samples.CollectionDate) AS CollectionDateTime, samples._CumulativeIdleTime, samples._CumulativeReadBytes, samples._CumulativeReads, samples._CumulativeReadTime, samples._CumulativeWriteBytes, samples._CumulativeWrites, samples._CumulativeWriteTime, samples._FreeBytes FROM [data].Cluster_Machine_LogicalDisk_UnstableSamples samples JOIN [data].Cluster_Machine_LogicalDisk_Keys logicalDisk ON samples.Id = logicalDisk.Id JOIN [data].Cluster_Machine_Keys machine ON logicalDisk.ParentId = machine.Id JOIN [data].Cluster_Keys cluster ON machine.ParentId = cluster.Id WHERE samples.CollectionDate > utils.DateTimeToTicks(DATEADD(minute, -3, GETUTCDATE())) ORDER BY cluster._Name, machine._Name, logicalDisk._Name, samples.CollectionDate; |
ClusterName | MachineName | LogicalDiskName | CollectionDateTime | _CumulativeIdleTime | _CumulativeReadBytes | _CumulativeReads | _CumulativeReadTime | _CumulativeWriteBytes | _CumulativeWrites | _CumulativeWriteTime | _FreeBytes | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | granger | C: | 2013-01-16 00:58:22.567 | 1049313057000 | 36469454848 | 597259 | 9361317000 | 26231646208 | 786208 | 1930692346000 | 19057868800 | |
2 | granger | C: | 2013-01-16 00:58:37.570 | 1049462333000 | 36469458944 | 597260 | 9361777000 | 26232695808 | 786341 | 1930694826000 | 19057868800 | |
3 | granger | C: | 2013-01-16 00:58:52.573 | 1049608951000 | 36469463040 | 597261 | 9361783000 | 26233865728 | 786497 | 1930702003000 | 19057868800 | |
4 | granger | C: | 2013-01-16 00:59:07.573 | 1049758677000 | 36469475328 | 597262 | 9361798000 | 26235663872 | 786596 | 1930703647000 | 19057868800 | |
5 | granger | C: | 2013-01-16 00:59:22.577 | 1049907173000 | 36469475328 | 597262 | 9361798000 | 26236491776 | 786711 | 1930707538000 | 19057868800 | |
6 | granger | C: | 2013-01-16 00:59:37.580 | 1050055364000 | 36469475328 | 597262 | 9361798000 | 26245685248 | 786809 | 1930710377000 | 19057868800 | |
7 | granger | C: | 2013-01-16 00:59:52.613 | 1050205726000 | 36469475328 | 597262 | 9361798000 | 26248634880 | 786957 | 1930711628000 | 19057868800 | |
8 | granger | C: | 2013-01-16 01:00:07.593 | 1050348049000 | 36469692928 | 597285 | 9365875000 | 26253995520 | 787173 | 1930722313000 | 19057868800 | |
9 | granger | C: | 2013-01-16 01:00:22.593 | 1050497774000 | 36469692928 | 597285 | 9365875000 | 26254995456 | 787319 | 1930724468000 | 19057868800 | |
10 | granger | C: | 2013-01-16 01:00:37.593 | 1050647344000 | 36469791232 | 597287 | 9366365000 | 26255652864 | 787396 | 1930725471000 | 19057868800 | |
11 | granger | C: | 2013-01-16 01:00:52.597 | 1050797180000 | 36469791232 | 597287 | 9366365000 | 26256245248 | 787473 | 1930727599000 | 19057868800 | |
12 | granger | C: | 2013-01-16 01:01:07.597 | 1050942870000 | 36472880640 | 597370 | 9371308000 | 26257645568 | 787590 | 1930731810000 | 19057868800 | |
13 | granger | D: | 2013-01-16 00:58:22.567 | 1391205223000 | 642137088 | 122300 | 3389923000 | 47351808 | 2229 | 4116500000 | 169272672256 | |
14 | granger | D: | 2013-01-16 00:58:37.570 | 1391356102000 | 642137088 | 122300 | 3389923000 | 47351808 | 2229 | 4116500000 | 169272672256 | |
15 | granger | D: | 2013-01-16 00:58:52.573 | 1391506965000 | 642137088 | 122300 | 3389923000 | 47351808 | 2229 | 4116500000 | 169272672256 | |
16 | granger | D: | 2013-01-16 00:59:07.573 | 1391657802000 | 642137088 | 122300 | 3389923000 | 47351808 | 2229 | 4116500000 | 169272672256 | |
17 | granger | D: | 2013-01-16 00:59:22.577 | 1391808658000 | 642137088 | 122300 | 3389923000 | 47351808 | 2229 | 4116500000 | 169272672256 | |
18 | granger | D: | 2013-01-16 00:59:37.580 | 1391959507000 | 642137088 | 122300 | 3389923000 | 47351808 | 2229 | 4116500000 | 169272672256 | |
19 | granger | D: | 2013-01-16 00:59:52.613 | 1392110681000 | 642137088 | 122300 | 3389923000 | 47351808 | 2229 | 4116500000 | 169272672256 | |
20 | granger | D: | 2013-01-16 01:00:07.593 | 1392261300000 | 642137088 | 122300 | 3389923000 | 47351808 | 2229 | 4116500000 | 169272672256 | |
21 | granger | D: | 2013-01-16 01:00:22.593 | 1392412146000 | 642137088 | 122300 | 3389923000 | 47351808 | 2229 | 4116500000 | 169272672256 | |
22 | granger | D: | 2013-01-16 01:00:37.593 | 1392562989000 | 642137088 | 122300 | 3389923000 | 47351808 | 2229 | 4116500000 | 169272672256 | |
23 | granger | D: | 2013-01-16 01:00:52.597 | 1392713828000 | 642137088 | 122300 | 3389923000 | 47351808 | 2229 | 4116500000 | 169272672256 | |
24 | granger | D: | 2013-01-16 01:01:07.597 | 1392864682000 | 642137088 | 122300 | 3389923000 | 47351808 | 2229 | 4116500000 | 169272672256 | |
25 | granger | HarddiskVolume1 | 2013-01-16 00:58:22.567 | 1396186769000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 | |
26 | granger | HarddiskVolume1 | 2013-01-16 00:58:37.570 | 1396337648000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 | |
27 | granger | HarddiskVolume1 | 2013-01-16 00:58:52.573 | 1396488511000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 | |
28 | granger | HarddiskVolume1 | 2013-01-16 00:59:07.573 | 1396639348000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 | |
29 | granger | HarddiskVolume1 | 2013-01-16 00:59:22.577 | 1396790204000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 | |
30 | granger | HarddiskVolume1 | 2013-01-16 00:59:37.580 | 1396941053000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 | |
31 | granger | HarddiskVolume1 | 2013-01-16 00:59:52.613 | 1397092227000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 | |
32 | granger | HarddiskVolume1 | 2013-01-16 01:00:07.593 | 1397242846000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 | |
33 | granger | HarddiskVolume1 | 2013-01-16 01:00:22.593 | 1397393693000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 | |
34 | granger | HarddiskVolume1 | 2013-01-16 01:00:37.593 | 1397544535000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 | |
35 | granger | HarddiskVolume1 | 2013-01-16 01:00:52.597 | 1397695374000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 | |
36 | granger | HarddiskVolume1 | 2013-01-16 01:01:07.597 | 1397846228000 | 254464 | 58 | 1925000 | 294912 | 71 | 787000 | 63963136 |
Well, for each _UnstableSamples table, there is a corresponding view suffixed with _UnstableSamples_View that already performs these joins and date conversions for you. The above query can be rewritten as follows, whilst producing exactly the same output albeit with slightly different column names.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
SELECT Cluster_Name, Cluster_Machine_Name, Cluster_Machine_LogicalDisk_Name, CollectionDate_DateTime, Cluster_Machine_LogicalDisk_CumulativeIdleTime, Cluster_Machine_LogicalDisk_CumulativeReadBytes, Cluster_Machine_LogicalDisk_CumulativeReads, Cluster_Machine_LogicalDisk_CumulativeReadTime, Cluster_Machine_LogicalDisk_CumulativeWriteBytes, Cluster_Machine_LogicalDisk_CumulativeWrites, Cluster_Machine_LogicalDisk_CumulativeWriteTime, Cluster_Machine_LogicalDisk_FreeBytes FROM [data].[Cluster_Machine_LogicalDisk_UnstableSamples_View] WHERE CollectionDate > utils.DateTimeToTicks(DATEADD(minute, -3, GETUTCDATE())) ORDER BY Cluster_Name, Cluster_Machine_Name, Cluster_Machine_LogicalDisk_Name, CollectionDate; |
Some more examples
Okay, I’ve covered all I wanted to for this post. I haven’t yet explored the _StableSamples or _Instances tables, but most of the interesting stuff is in the _UnstableSamples tables I’ve already described. Now it’s time for a few more examples.
CPU performance monitoring data
The raw data from which the CPU performance metrics in SQL monitor are all derived can be found in the monitoringCluster_Machine_Processors_UnstableSamples table, but it’s easier to examine the corresponding view, in this case to see what’s been going on in the most recent 3 minutes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
SELECT [Cluster_Name], [Cluster_Machine_Name], [CollectionDate_DateTime], [Cluster_Machine_Processors_AverageQueueLength], [Cluster_Machine_Processors_CumulativeAverageContextSwitches], [Cluster_Machine_Processors_CumulativeDpcTime], [Cluster_Machine_Processors_CumulativeIdleTime], [Cluster_Machine_Processors_CumulativeInterruptTime], [Cluster_Machine_Processors_CumulativePrivilegedTime], [Cluster_Machine_Processors_CumulativeUserTime] FROM [data].[Cluster_Machine_Processors_UnstableSamples_View] WHERE CollectionDate > utils.DateTimeToTicks(DATEADD(minute, -3, GETUTCDATE())) ORDER BY [Cluster_Name], [Cluster_Machine_Name], [CollectionDate]; |
Cluster_Name | Cluster_Machine_Name | CollectionDate_DateTime | Cluster_Machine_Processors_AverageQueueLength | Cluster_Machine_Processors_CumulativeAverageContextSwitches | Cluster_Machine_Processors_CumulativeDpcTime | Cluster_Machine_Processors_CumulativeIdleTime | Cluster_Machine_Processors_CumulativeInterruptTime | Cluster_Machine_Processors_CumulativePrivilegedTime | Cluster_Machine_Processors_CumulativeUserTime | |
---|---|---|---|---|---|---|---|---|---|---|
1 | granger | 2013-01-16 10:17:24.707 | 0 | 123225982 | 444368849 | 1304351817175 | 2395024850 | 23053281775 | 39212684362 | |
2 | granger | 2013-01-16 10:17:39.720 | 0 | 123236935 | 444466349 | 1304498711617 | 2395297851 | 23055192787 | 39214107871 | |
3 | granger | 2013-01-16 10:17:54.720 | 0 | 123247119 | 444505350 | 1304646503064 | 2395434352 | 23056421295 | 39215004877 | |
4 | granger | 2013-01-16 10:18:09.720 | 0 | 123257612 | 444544350 | 1304791778995 | 2395531853 | 23058800310 | 39217422893 | |
5 | granger | 2013-01-16 10:18:24.723 | 0 | 123269326 | 444583350 | 1304937893432 | 2395746354 | 23060925824 | 39219099903 | |
6 | granger | 2013-01-16 10:18:39.733 | 0 | 123279756 | 444622350 | 1305085606879 | 2396058356 | 23062466333 | 39220074910 | |
7 | granger | 2013-01-16 10:18:54.733 | 0 | 123291363 | 444641850 | 1305231389813 | 2396194857 | 23063694841 | 39222980428 | |
8 | granger | 2013-01-16 10:19:09.743 | 0 | 123301483 | 444680851 | 1305377816252 | 2396467859 | 23064650347 | 39225671445 | |
9 | granger | 2013-01-16 10:19:24.750 | 0 | 123313007 | 444739351 | 1305524866695 | 2396721360 | 23066112857 | 39227231455 | |
10 | granger | 2013-01-16 10:19:39.757 | 0 | 123323596 | 444758851 | 1305672716642 | 2396916362 | 23066970862 | 39228596464 | |
11 | granger | 2013-01-16 10:19:54.757 | 0 | 123335291 | 444856352 | 1305819669584 | 2397072363 | 23068355371 | 39230331975 | |
12 | granger | 2013-01-16 10:20:09.757 | 0 | 123346296 | 444934352 | 1305965725521 | 2397267364 | 23069798380 | 39232749991 |
All of these raw values correspond to the raw Windows Performance Counters provided by Windows. Just like the time-based properties we’ve explored in Cluster_Machine_LogicalDisk_UnstableSamples, most of the properties in Cluster_Machine_Processors_UnstableSamples are cumulative time samples, based on 100 nanosecond units.
SQL Monitor actually only displays two metrics derived from these values:
- Avg. CPU queue length – This corresponds directly to the Cluster_Machine_Processors_AverageQueueLength column in the above query.
- Machine: processor time – This is trivially derived from the Cluster_Machine_Processors_CumulativeIdleTime column in the above query (i.e. % cpu usage = 100 – % idle time).
SQL Server statistics
The Cluster_SqlServer_SqlStatistics_UnstableSamples table contains the raw monitoring data for the Batch requests/sec, Compilations/sec and Compilations/batch metrics displayed in SQL Monitor’s Analysis tab. Let’s have a look at the corresponding view, to see what’s been going on in the most recent 3 minutes.
1 2 3 4 5 6 7 8 9 10 11 |
SELECT Cluster_Name, Cluster_SqlServer_Name, CollectionDate_DateTime, Cluster_SqlServer_SqlStatistics_CumulativeBatchRequests, Cluster_SqlServer_SqlStatistics_CumulativeCompilations, Cluster_SqlServer_SqlStatistics_CumulativeRecompilations FROM [data].[Cluster_SqlServer_SqlStatistics_UnstableSamples_View] WHERE [CollectionDate] > utils.DateTimeToTicks(DATEADD(minute, -3, GETUTCDATE())) ORDER BY Cluster_Name, Cluster_SqlServer_Name, CollectionDate; |
1 | Cluster_Name | Cluster_SqlServer_Name | CollectionDate_DateTime | Cluster_SqlServer_SqlStatistics_CumulativeBatchRequests | Cluster_SqlServer_SqlStatistics_CumulativeCompilations | Cluster_SqlServer_SqlStatistics_CumulativeRecompilations |
---|---|---|---|---|---|---|
2 | dev-chrisl2 | 2013-01-16 11:33:20.003 | 183787 | 279705 | 23198 | |
3 | dev-chrisl2 | 2013-01-16 11:33:35.023 | 183818 | 279752 | 23199 | |
4 | dev-chrisl2 | 2013-01-16 11:33:50.037 | 183879 | 279850 | 23212 | |
5 | dev-chrisl2 | 2013-01-16 11:34:05.047 | 183939 | 279946 | 23223 | |
6 | dev-chrisl2 | 2013-01-16 11:34:20.043 | 183955 | 279960 | 23223 | |
7 | dev-chrisl2 | 2013-01-16 11:34:35.070 | 183986 | 280007 | 23224 | |
8 | dev-chrisl2 | 2013-01-16 11:34:50.077 | 184035 | 280089 | 23235 | |
9 | dev-chrisl2 | 2013-01-16 11:35:05.080 | 184053 | 280105 | 23235 | |
10 | dev-chrisl2 | 2013-01-16 11:35:20.083 | 184067 | 280117 | 23235 | |
11 | dev-chrisl2 | 2013-01-16 11:35:35.087 | 184098 | 280164 | 23236 | |
12 | dev-chrisl2 | 2013-01-16 11:35:50.090 | 184151 | 280249 | 23247 | |
13 | dev-chrisl2 | 2013-01-16 11:36:05.087 | 184167 | 280263 | 23247 | |
14 | dev-chrisl2 | sql2005 | 2013-01-16 11:33:20.003 | 2331133 | 362286 | 25574 |
15 | dev-chrisl2 | sql2005 | 2013-01-16 11:33:35.023 | 2331672 | 362346 | 25575 |
16 | dev-chrisl2 | sql2005 | 2013-01-16 11:33:50.037 | 2332289 | 362464 | 25588 |
17 | dev-chrisl2 | sql2005 | 2013-01-16 11:34:05.047 | 2332817 | 362534 | 25598 |
18 | dev-chrisl2 | sql2005 | 2013-01-16 11:34:20.043 | 2333240 | 362561 | 25598 |
19 | dev-chrisl2 | sql2005 | 2013-01-16 11:34:35.070 | 2333722 | 362623 | 25599 |
20 | dev-chrisl2 | sql2005 | 2013-01-16 11:34:50.077 | 2334276 | 362730 | 25610 |
21 | dev-chrisl2 | sql2005 | 2013-01-16 11:35:05.080 | 2334771 | 362765 | 25610 |
22 | dev-chrisl2 | sql2005 | 2013-01-16 11:35:20.083 | 2335193 | 362796 | 25610 |
23 | dev-chrisl2 | sql2005 | 2013-01-16 11:35:35.087 | 2335667 | 362856 | 25611 |
24 | dev-chrisl2 | sql2005 | 2013-01-16 11:35:50.090 | 2336249 | 362959 | 25622 |
25 | dev-chrisl2 | sql2005 | 2013-01-16 11:36:05.087 | 2336744 | 362986 | 25622 |
26 | dev-chrisl2 | sql2008 | 2013-01-16 11:33:14.557 | 199300 | 320438 | 38234 |
27 | dev-chrisl2 | sql2008 | 2013-01-16 11:33:29.560 | 199465 | 320581 | 38259 |
28 | dev-chrisl2 | sql2008 | 2013-01-16 11:33:44.563 | 199562 | 320741 | 38300 |
29 | dev-chrisl2 | sql2008 | 2013-01-16 11:33:59.570 | 199605 | 320796 | 38324 |
30 | dev-chrisl2 | sql2008 | 2013-01-16 11:34:14.587 | 199645 | 320845 | 38344 |
31 | dev-chrisl2 | sql2008 | 2013-01-16 11:34:29.593 | 199700 | 320934 | 38369 |
32 | dev-chrisl2 | sql2008 | 2013-01-16 11:34:44.590 | 199813 | 321131 | 38422 |
33 | dev-chrisl2 | sql2008 | 2013-01-16 11:34:59.617 | 199902 | 321209 | 38446 |
34 | dev-chrisl2 | sql2008 | 2013-01-16 11:35:14.600 | 199938 | 321258 | 38466 |
35 | dev-chrisl2 | sql2008 | 2013-01-16 11:35:29.603 | 199991 | 321345 | 38491 |
36 | dev-chrisl2 | sql2008 | 2013-01-16 11:35:44.613 | 200074 | 321487 | 38530 |
37 | dev-chrisl2 | sql2008 | 2013-01-16 11:35:59.617 | 200112 | 321541 | 38554 |
38 | dev-chrisl2 | sql2012 | 2013-01-16 11:33:14.460 | 183808 | 283703 | 23199 |
39 | dev-chrisl2 | sql2012 | 2013-01-16 11:33:29.460 | 183837 | 283751 | 23200 |
40 | dev-chrisl2 | sql2012 | 2013-01-16 11:33:44.463 | 183900 | 283866 | 23213 |
41 | dev-chrisl2 | sql2012 | 2013-01-16 11:33:59.450 | 183917 | 283880 | 23213 |
42 | dev-chrisl2 | sql2012 | 2013-01-16 11:34:14.470 | 183935 | 283896 | 23213 |
43 | dev-chrisl2 | sql2012 | 2013-01-16 11:34:29.500 | 183964 | 283942 | 23214 |
44 | dev-chrisl2 | sql2012 | 2013-01-16 11:34:44.483 | 184045 | 284088 | 23235 |
45 | dev-chrisl2 | sql2012 | 2013-01-16 11:34:59.483 | 184061 | 284103 | 23235 |
46 | dev-chrisl2 | sql2012 | 2013-01-16 11:35:14.487 | 184077 | 284117 | 23235 |
47 | dev-chrisl2 | sql2012 | 2013-01-16 11:35:29.517 | 184106 | 284163 | 23236 |
48 | dev-chrisl2 | sql2012 | 2013-01-16 11:35:44.513 | 184161 | 284266 | 23247 |
49 | dev-chrisl2 | sql2012 | 2013-01-16 11:35:59.500 | 184175 | 284279 | 23247 |
The SQL Monitor metrics are derived from this table as follows:
- Batch requests/sec is based on the increase in Cluster_SqlServer_SqlStatistics_CumulativeBatchRequests between successive samples.
- Compilations/sec is based on the increase in Cluster_SqlServer_SqlStatistics_CumulativeCompilations between successive samples.
- Compilations/batch is based on the increase in Cluster_SqlServer_SqlStatistics_CumulativeCompilations divided by the increase in Cluster_SqlServer_SqlStatistics_CumulativeBatchRequests between successive samples.
If you want to calculate Compilations/batch in your own queries, don’t forget to handle the possible “divide by zero” error in the case where there are no cumulative batch requests within any time interval your query is concerned with.
Custom metrics
Retrieving the raw monitoring data for custom metrics is a little different to all of the other metrics, mainly because Cluster_SqlServer_Database_CustomMetric_UnstableSamples_View only contains a Cluster_SqlServer_Database_CustomMetric_MetricId property, not the actual custom metric name. You can still make use of this view, but a single join to another table is required in order to include the name of the custom metric in the results. Here are all of the custom metrics that have been running on my laptop for the past 3 minutes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
SELECT samples.Cluster_Name AS Cluster, samples.Cluster_SqlServer_Name AS SqlServer, samples.Cluster_SqlServer_Database_Name AS [Database], customMetrics.Name AS CustomMetric, samples.CollectionDate_DateTime AS CollectionDate, samples.Cluster_SqlServer_Database_CustomMetric_Value AS Value FROM [data].[Cluster_SqlServer_Database_CustomMetric_UnstableSamples_View] samples JOIN [settings].[CustomMetrics] customMetrics ON samples.Cluster_SqlServer_Database_CustomMetric_MetricId = customMetrics.Id WHERE samples.CollectionDate > utils.DateTimeToTicks(DATEADD(minute, -3, GETUTCDATE())) ORDER BY samples.Cluster_Name, samples.Cluster_SqlServer_Name, samples.Cluster_SqlServer_Database_Name, customMetrics.Name; |
Cluster | SqlServer | Database | CustomMetric | CollectionDate | Value | |
---|---|---|---|---|---|---|
1 | granger | Debacle | Percentage of database free space | 2013-01-16 13:43:11.243 | 61.71875 | |
2 | granger | Debacle | Percentage of database free space | 2013-01-16 13:44:11.320 | 61.71875 | |
3 | granger | Debacle | Percentage of database free space | 2013-01-16 13:45:11.263 | 61.71875 | |
4 | granger | SqlMonitorData | Percentage of database free space | 2013-01-16 13:43:11.237 | 2.794894 | |
5 | granger | SqlMonitorData | Percentage of database free space | 2013-01-16 13:44:11.313 | 2.75088 | |
6 | granger | SqlMonitorData | Percentage of database free space | 2013-01-16 13:45:11.257 | 2.728873 | |
7 | granger | master | Percentage of database free space | 2013-01-16 13:43:11.217 | 48.4375 | |
8 | granger | master | Percentage of database free space | 2013-01-16 13:44:11.267 | 48.4375 | |
9 | granger | master | Percentage of database free space | 2013-01-16 13:45:11.237 | 48.4375 | |
10 | granger | model | Percentage of database free space | 2013-01-16 13:43:11.227 | 53.819444 | |
11 | granger | model | Percentage of database free space | 2013-01-16 13:44:11.297 | 53.819444 | |
12 | granger | model | Percentage of database free space | 2013-01-16 13:45:11.247 | 53.819444 | |
13 | granger | msdb | Percentage of database free space | 2013-01-16 13:43:11.233 | 13.701923 | |
14 | granger | msdb | Percentage of database free space | 2013-01-16 13:44:11.307 | 13.701923 | |
15 | granger | msdb | Percentage of database free space | 2013-01-16 13:45:11.253 | 13.701923 | |
16 | granger | tempdb | Cached pages in TempDB (MB) | 2013-01-16 13:43:11.210 | 0 | |
17 | granger | tempdb | Cached pages in TempDB (MB) | 2013-01-16 13:44:11.260 | 0 | |
18 | granger | tempdb | Cached pages in TempDB (MB) | 2013-01-16 13:45:11.233 | 0 | |
19 | granger | tempdb | Percentage of database free space | 2013-01-16 13:43:11.220 | 55.190058 | |
20 | granger | tempdb | Percentage of database free space | 2013-01-16 13:44:11.273 | 55.116959 | |
21 | granger | tempdb | Percentage of database free space | 2013-01-16 13:45:11.240 | 55.116959 | |
22 | granger | tempdb | Plan cache hit ratio | 2013-01-16 13:43:11.167 | 79.1 | |
23 | granger | tempdb | Plan cache hit ratio | 2013-01-16 13:44:11.193 | 80.39 | |
24 | granger | tempdb | Plan cache hit ratio | 2013-01-16 13:45:11.190 | 79 | |
25 | granger | tempdb | Plan cache reuse | 2013-01-16 13:43:11.203 | 39.4994927291174 | |
26 | granger | tempdb | Plan cache reuse | 2013-01-16 13:44:11.253 | 39.4994927291174 | |
27 | granger | tempdb | Plan cache reuse | 2013-01-16 13:45:11.227 | 39.4994927291174 |
The interpretation of the raw custom metric values obviously vary from metric to metric. For any metrics imported from Red Gate’s own SQL Monitor Metrics web-site, you should consult the individual metric descriptions. For your own metrics, I hope you already know how they work!
Right, that’s it for this post. I hope you’re inspired to investigate some of the other _UnstableSamples tables to see what other raw data is available. If you have any questions, please either leave a comment or email me directly (chris.lambrou@red-gate.com) and I’ll see what I can do to help.
Load comments