...
 
Commits (2)
/*
--Creates PerformanceCollector db if it does not exist, and only if it was requested. The .ps1 deploy script determines if this should be called.
--Makes no attempt to install the db files on anything but the default file locations. YOU SHOULD ALTER THIS.
--You can always build the PerformanceCollector db yourself, in which case this script does nothing. Then you can customize it to suit your needs and
standards.
*/
SET NOCOUNT ON
a separate PerformanceCollector db won't work quite right in SQL Azure. Instead, each db needs its own PerformanceCollector schema and associated
"jobs"
IF NOT EXISTS (SELECT 1 FROM master..sysdatabases WHERE NAME = 'PerformanceCollector')
BEGIN
PRINT 'Database is not found. Database will be created.'
DECLARE @SQL VARCHAR(MAX);
-------------------------------------------------------------
-- Build and execute the Create Database command
-------------------------------------------------------------
PRINT 'Executing the Create Database command...'
SELECT @SQL = '
CREATE DATABASE PerformanceCollector'
PRINT @SQL
BEGIN TRY
EXEC(@SQL)
EXEC ('ALTER DATABASE PerformanceCollector SET RECOVERY SIMPLE');
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
PRINT ERROR_NUMBER()
PRINT 'The above error occurred while trying to CREATE DATABASE. '
RAISERROR ('See previous errors',16,1)
END CATCH
END
GO
USE PerformanceCollector
GO
EXEC sp_changedbowner 'sa'
GO
*/
--add the PerformanceCollector Job Category
--if the @CategoryName ever changes we need to fix *all* job creation scripts. All of that is hardcoded as well. It would be nice
--if someone fixed this.
DECLARE @CategoryName varchar(60)
SELECT @CategoryName = 'PerformanceCollector'
IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name = @CategoryName AND category_class=1)
BEGIN
EXEC msdb.dbo.sp_add_category
@class=N'JOB',
@type=N'LOCAL',
@name= @CategoryName
END
GO
\ No newline at end of file
--job categories and jobs will not work in SQLAzure
\ No newline at end of file
BEGIN TRANSACTION
DECLARE @CurrDatabase varchar(120)
SELECT @CurrDatabase = db_name()
DECLARE @JobID BINARY(16)
DECLARE @ReturnCode INT
SELECT @ReturnCode = 0
-- Delete the job with the same name (if it exists)
SELECT @JobID = job_id
FROM msdb.dbo.sysjobs
WHERE (name = N'PerformanceCollector BlockAndWaitMonitor')
IF (@JobID IS NOT NULL)
BEGIN
-- Check if the job is a multi-server job
IF (EXISTS (SELECT *
FROM msdb.dbo.sysjobservers
WHERE (job_id = @JobID) AND (server_id <> 0)))
BEGIN
-- There is, so abort the script
RAISERROR (N'Unable to import job ''PerformanceCollector BlockAndWaitMonitor'' since there is already a multi-server job with this name.', 16, 1)
GOTO QuitWithRollback
END
ELSE
-- Delete the [local] job
EXECUTE msdb.dbo.sp_delete_job @job_name = N'PerformanceCollector BlockAndWaitMonitor'
SELECT @JobID = NULL
END
BEGIN
-- Add the job
EXECUTE @ReturnCode = msdb.dbo.sp_add_job @job_id = @JobID OUTPUT ,
@job_name = N'PerformanceCollector BlockAndWaitMonitor',
@owner_login_name = N'sa',
@description = N'Runs every 15 seconds and collects data from any spid involved in blocking or waiting on the instance.',
@category_name=N'PerformanceCollector',
@enabled = 1, @notify_level_email = 0, @notify_level_page = 0, @notify_level_netsend = 0, @notify_level_eventlog = 2, @delete_level= 0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
-- Add the job steps
EXECUTE @ReturnCode = msdb.dbo.sp_add_jobstep @job_id = @JobID, @step_id = 1, @step_name = N'PerformanceCollector BlockAndWaitMonitor', @command = N'
DECLARE @iPollingInterval int
DECLARE @SQL varchar(400)
WHILE 1=1
BEGIN
SELECT @iPollingInterval=ConfigValueInt
FROM PerformanceCollector.Config
WHERE ConfigSetting = ''PollingInterval''
EXEC PerformanceCollector.WaitAndBlockInspector;
SELECT @SQL=''WAITFOR DELAY ''''00:00:''+convert(varchar(2),@iPollingInterval)+''''''''
exec(@SQL)
END', @database_name = @CurrDatabase , @server = N'', @database_user_name = N'', @subsystem = N'TSQL', @cmdexec_success_code = 0, @flags = 0, @retry_attempts = 0, @retry_interval = 1, @output_file_name = N'', @on_success_step_id = 0, @on_success_action = 1, @on_fail_step_id = 0, @on_fail_action = 2
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXECUTE @ReturnCode = msdb.dbo.sp_update_job @job_id = @JobID, @start_step_id = 1
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
-- Add the job schedules
EXECUTE @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id = @JobID, @name = N'Sched', @enabled = 1, @freq_type = 4, @active_start_date = 20090313, @active_start_time = 0, @freq_interval = 1, @freq_subday_type = 4, @freq_subday_interval = 5, @freq_relative_interval = 0, @freq_recurrence_factor = 0, @active_end_date = 99991231, @active_end_time = 235959
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
-- Add the Target Servers
EXECUTE @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @JobID, @server_name = N'(local)'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
END
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION
EndSave:
--jobs do not exist in SQLAzure
\ No newline at end of file
/*
Builds the AddIns job. This runs every 5 mins and looks to the PerformanceCollector.Config for
any work that is scheduled to be done.
*/
BEGIN TRANSACTION
DECLARE @CurrDatabase varchar(120)
SELECT @CurrDatabase = db_name()
DECLARE @JobID BINARY(16)
DECLARE @ReturnCode INT
SELECT @ReturnCode = 0
-- Delete the job with the same name (if it exists)
SELECT @JobID = job_id
FROM msdb.dbo.sysjobs
WHERE (name = N'PerformanceCollector.RunAddIns')
IF (@JobID IS NOT NULL)
BEGIN
-- Check if the job is a multi-server job
IF (EXISTS (SELECT *
FROM msdb.dbo.sysjobservers
WHERE (job_id = @JobID) AND (server_id <> 0)))
BEGIN
-- There is, so abort the script
RAISERROR (N'Unable to import job ''PerformanceCollector.RunAddIns'' since there is already a multi-server job with this name.', 16, 1)
GOTO QuitWithRollback
END
ELSE
-- Delete the [local] job
EXECUTE msdb.dbo.sp_delete_job @job_name = N'PerformanceCollector.RunAddIns'
SELECT @JobID = NULL
END
BEGIN
-- Add the job
EXECUTE @ReturnCode = msdb.dbo.sp_add_job @job_id = @JobID OUTPUT ,
@job_name = N'PerformanceCollector.RunAddIns',
@owner_login_name = N'sa',
@description = N'This runs every 5 minutes and determines what performance metrics are scheduled for collection.',
@category_name=N'PerformanceCollector',
@enabled = 1, @notify_level_email = 0, @notify_level_page = 0, @notify_level_netsend = 0, @notify_level_eventlog = 2, @delete_level= 0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
-- Add the job steps
EXECUTE @ReturnCode = msdb.dbo.sp_add_jobstep @job_id = @JobID, @step_id = 1, @step_name = N'PerformanceCollector.RunAddIns', @command = N'EXEC PerformanceCollector.RunAddIns',
@database_name = @CurrDatabase , @server = N'', @database_user_name = N'', @subsystem = N'TSQL', @cmdexec_success_code = 0, @flags = 0, @retry_attempts = 0, @retry_interval = 1, @output_file_name = N'', @on_success_step_id = 0, @on_success_action = 1, @on_fail_step_id = 0, @on_fail_action = 2
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXECUTE @ReturnCode = msdb.dbo.sp_update_job @job_id = @JobID, @start_step_id = 1
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
-- Add the job schedules
EXECUTE @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id = @JobID, @name = N'Sched', @enabled = 1, @freq_type = 4, @active_start_date = 20090313, @active_start_time = 0, @freq_interval = 1, @freq_subday_type = 4, @freq_subday_interval = 5, @freq_relative_interval = 0, @freq_recurrence_factor = 0, @active_end_date = 99991231, @active_end_time = 235959
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
-- Add the Target Servers
EXECUTE @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @JobID, @server_name = N'(local)'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
END
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION
EndSave:
--jobs do not work in SQL Azure for asynchronous scheduling
\ No newline at end of file
......@@ -5,8 +5,8 @@ This utility captures a series of performance metrics and logs them to various t
Git Branches
================
# master: this code works through SQL 2016
# SQLAzure: this is the same code as master except it does not use SQL Agent nor any of the DMVs that are not available in Azure.
* master: this code works through SQL 2016
* SQLAzure: this is the same code as master except it does not use SQL Agent nor any of the DMVs that are not available in Azure.
More on this below.
......@@ -16,10 +16,22 @@ How to Install Performance Collector
2. Create the PerformanceCollector database on your server using your preferred file layouts. If you don't do this the Installer script will do this for you using the default data file and log placement.
3. Run PerformanceCollectorInstaller.sql as sysadmin.
SQLAzure Branch
================
Running Performance Collector on SQLAzure
==========================================
If you want to run PC on SQL Azure you need to switch to the SQLAzure branch.
SQLAzure is a PaaS offering for SQL Server (if you are running SQL Server in a VM in Azure, known as IaaS, then continue to use the master branch code). For the most part PC works great on SQL Azure but there are some differences regarding how SQL Azure databases work:
* There is no concept of SQL Agent (asynchronous job scheduler). We have to use something else. This is not coded, you'll need to find the solution. There are multiple Azure tools that accomplish scheduled tasks, but nothing works really well given the unique requirements of PC. For instance, Azure Functions is the "cron for Azure" solution but it isn't meant to launch a task that never completes. Using AF for this is VERY expensive. Azure Scheduler and Azure Automation have similar limitations as of when this was written.
* SQL "Servers" are used for logical groupings of SQL "Databases" only. You can theoretically put EVERY SQL Database on the same Server and Azure's "fabric" will manage resources for you. You declare how many DTUs (Database Throughput Units) you need for a given database, not for a server. For PC, this means that having a separate PerformanceCollector db on your SQLAzure Server is meaningless. Instead, PC installs as a schema on your existing SQLAzure db and monitors that. The side effect is that if you have 50 databases you are running 50 PerformanceCollector schemas and jobs. There is no good way around this.
* In the future it may be worth snapping sys.dm_db_resource_stats to track approximate DTUs for elastic scaling of SQLAzure.
* I have not tested this with SQL Azure Data Warehouse (SQLDW) but it should work mostly as expected. [Monitor Your Workload Using DMVs](https://azure.microsoft.com/en-us/documentation/articles/sql-data-warehouse-manage-monitor/)
Caveats
------------
PerformanceCollector has not been tested with Azure SQL Datawarehouse (SQLDW).
Making Changes to Performance Collector
=========================================
PC is installed using PerformanceCollectorInstaller.sql. However, that's a big file to update so it's easier to add new files and change existing code in the actual .sql files in the various folders. Change whatever you need to change and then run `powershell .\BuildSQL.ps1`. This command will rebuild PerformanceCollectorInstaller.sql with your latest edits.
......
......@@ -5,8 +5,8 @@ END
ELSE
BEGIN
UPDATE PerformanceCollector.Version SET
ID = '1.2',
ID = '0.8',
ApplyDTime = GETDATE(),
CntcName = 'PerformanceCollector',
LastCngText = 'Upgrade'
LastCngText = 'SQLAzureVer'
END
......@@ -24,22 +24,17 @@ BEGIN
INSERT INTO PerformanceCollector.IOStalls (CurrTime, ServerName, DbName, FileType, num_of_writes,
num_of_reads, num_of_bytes_written, num_of_bytes_read, io_stall_write_ms, io_stall_read_ms,
io_stall, ReadLatency, WriteLatency, Latency, Drive,PhysicalFileName)
SELECT GETDATE(),@@SERVERNAME,db_name(vfs.database_id) as db,case when mf.type = 1 THEN 'log' else 'data' end as file_type,vfs.num_of_writes,
SELECT GETDATE(),@@SERVERNAME,db_name(vfs.database_id) as db,case when file_id = 1 THEN 'log' else 'data' end as file_type,vfs.num_of_writes,
vfs.num_of_reads,vfs.num_of_bytes_written, vfs.num_of_bytes_read, vfs.io_stall_write_ms, vfs.io_stall_read_ms,
vfs.io_stall,
CASE WHEN vfs.[num_of_reads] = 0 THEN 0 ELSE (vfs.[io_stall_read_ms] / vfs.[num_of_reads]) END,
CASE WHEN vfs.[num_of_writes] = 0 THEN 0 ELSE (vfs.[io_stall_write_ms] / vfs.[num_of_writes]) END,
CASE WHEN (vfs.[num_of_reads] = 0 AND vfs.[num_of_writes] = 0) THEN 0 ELSE (vfs.[io_stall] / (vfs.[num_of_reads] + vfs.[num_of_writes])) END,
LEFT (mf.[physical_name], 2) AS [Drive],
mf.[physical_name]
CASE WHEN vfs.[num_of_reads] = 0 THEN 0 ELSE (vfs.[io_stall_read_ms] / vfs.[num_of_reads]) END AS ReadLatency,
CASE WHEN vfs.[num_of_writes] = 0 THEN 0 ELSE (vfs.[io_stall_write_ms] / vfs.[num_of_writes]) END AS WriteLatency,
CASE WHEN (vfs.[num_of_reads] = 0 AND vfs.[num_of_writes] = 0) THEN 0 ELSE (vfs.[io_stall] / (vfs.[num_of_reads] + vfs.[num_of_writes])) END AS ReadWriteLatency,
NULL AS [Drive],
NULL
FROM
sys.dm_io_virtual_file_stats (NULL,NULL) AS vfs
JOIN sys.master_files AS mf
ON vfs.[database_id] = mf.[database_id]
AND vfs.[file_id] = mf.[file_id]
WHERE vfs.database_id = db_id()
END;
END
GO
\ No newline at end of file
......@@ -21,8 +21,7 @@ WHERE ConfigSetting = 'Addin_PerfCounters'
if datediff (hh,COALESCE(@nowprev,'1/1/1980'),@now) > @PollingInterval
BEGIN
--Determine the prefix for the Object_name field
declare @servicename varchar(200)
select @servicename=case when @@servicename<>'mssqlserver' then 'MSSQL$' + @@servicename +':' else 'sqlserver:' end
--this won't work correctly in SQLAzure
Create table #PerfCounters
(Currtime datetime
......@@ -38,8 +37,8 @@ BEGIN
(Currtime, ServerName,object_name, counter_name, cntr_value)
SELECT @Now,@@Servername,object_name, counter_name, cntr_value
FROM sys.dm_os_performance_counters
where object_name = @servicename+'Memory Manager'
OR ( object_name = @servicename+'SQL Statistics'
where object_name LIKE '%Memory Manager%'
OR ( object_name LIKE '%SQL Statistics%'
and counter_name in ('Batch Requests/sec'
,'SQL Compilations/sec'
,'SQL Re-Compilations/sec'
......@@ -50,7 +49,7 @@ BEGIN
INSERT INTO #PerfCounters
SELECT @Now,*
FROM sys.dm_os_performance_counters
where object_name = @servicename+'Buffer Manager'
where object_name like '%Buffer Manager%'
and counter_name in (
'Checkpoint pages/sec',
'Lazy writes/sec',
......
......@@ -58,7 +58,7 @@ DECLARE @OUTPUT TABLE (
p.stmt_start,
p.stmt_end,
act.name AS TransactionName
FROM master.dbo.sysprocesses p WITH (NOLOCK)
FROM sysprocesses p WITH (NOLOCK)
LEFT JOIN sys.dm_exec_requests r WITH (NOLOCK)
ON p.spid = r.session_id
LEFT JOIN sys.dm_tran_session_transactions st
......
BEXEC PerformanceCollector.RunAddins