Getting your Trinity Audio player ready...
|
A algumas semanas tivemos a necessidade de substituir a forma que usávamos para monitorar Dead Lock por alguma coisa mais usual…
Antes o Dead Lock era gravado no errorlog e depois encaminhado por email para o time,,, isso não é usual já que fica colocando muita informação desnecessária no errorlog e depois de alguns restarts perde-se algum histórico…
Com isso, cheguei a conclusão,,, vamos usar o event notification….
A vantagem é ser muito menos intrusivo que uma trigger e bem mais fácil de trabalhar que o System_Health…
Qual a ideia:
- O Event Notification vai enviar um email para cada vez que o evento de DeadLock acontecer, neste email vai conter informação da base que sofreu o DeadLock, horário e o DeadLock Graphic.
- Vai gravar em uma tabela na base de administração do DBA informações como: data e hora, base, XML com o evento.
use master go /*você já deve ter uma base de administração, caso não tenha, crie uma e troque o nome*/ alter database BASE_DE_ADMINISTRACAO_DO_DBA set enable_broker with rollback immediate go alter database BASE_DE_ADMINISTRACAO_DO_DBA set TRUSTWORTHY on go use BASE_DE_ADMINISTRACAO_DO_DBA go /**criando a tabela de controle/ create TABLE [dbo].[SQLDeadlockEvents]( [EventRowID] [bigint] primary key IDENTITY(1,1) NOT NULL, [EventType] [nvarchar](128) NOT NULL, [AlertTime] [datetime] NULL, [DeadlockGraph] [xml] NULL, [DatabaseName] [sysname] null, [AuditDate] [smalldatetime] ) ON [FG_Data_01] with (data_compression = PAGE) --neste caso tenho EE se você usar SE comente esta linha GO /**Criando a fila do Broker/ CREATE QUEUE [DeadlockGraphQueue] GO /*Criando o serviço para a fila do Broker*/ CREATE SERVICE [DeadlockEventsService] AUTHORIZATION [dbo] ON QUEUE [dbo].[DeadLockGraphQueue] ([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]) GO /*criando rota*/ CREATE ROUTE DeadlockGraphRoute WITH SERVICE_NAME = 'DeadlockEventsService', ADDRESS = 'LOCAL'; GO /* Para ficar mais fácil ele vai criar o event notification com os dados dinamicamente, se não, eu teria que explicar que o guid altera, onde tem que pegar essa informação,,, era mais SQL para uma coisa que pode ser feita de forma dinâmica */ DECLARE @AuditServiceBrokerGuid [uniqueidentifier] ,@SQL [varchar](max); -- broker guid do CaptureDeadlockGraph SELECT @AuditServiceBrokerGuid = [service_broker_guid] FROM [master].[sys].[databases] WHERE [name] = 'BASE_DE_ADMINISTRACAO_DO_DBA' SET @SQL = 'IF EXISTS (SELECT * FROM sys.server_event_notifications WHERE name = ''DeadlockGraphEventNotification'') DROP EVENT NOTIFICATION DeadlockGraphEventNotification ON SERVER CREATE EVENT NOTIFICATION DeadlockGraphEventNotification ON SERVER --aqui é o detalhe legal... servidor e não database WITH fan_in FOR DEADLOCK_GRAPH TO SERVICE ''DeadlockEventsService'', ''' + CAST(@AuditServiceBrokerGuid AS [varchar](50)) + ''';' EXEC (@SQL) GO /*Para confirmar que foi criado,,, lembra que é nível servidor,,, por isso a consulta no [sys].[server_event_notifications] */ SELECT * FROM [sys].[server_event_notifications] WHERE [name] = 'DeadlockGraphEventNotification'; GO /* onde a mágica acontece.... */ create PROC [dbo].[usp_sCaptureDeadlockGraph] AS BEGIN SET NOCOUNT ON SET CONCAT_NULL_YIELDS_NULL ON SET ANSI_PADDING ON SET ANSI_WARNINGS ON DECLARE @SQLProcedureName [varchar](64) SET @SQLProcedureName = 'usp_sCaptureDeadlockGraph' BEGIN TRY DECLARE @message_body [xml] , @EventTime [datetime] , @EventType [varchar](128) , @message_type_name [nvarchar](256) , @dialog [uniqueidentifier] -- Endless loop WHILE (1 = 1) BEGIN BEGIN TRANSACTION ; -- Receive the next available message WAITFOR (RECEIVE TOP(1) @message_type_name = [message_type_name], @message_body = [message_body], @dialog = [conversation_handle] FROM [dbo].[DeadlockGraphQueue]), TIMEOUT 2000 -- Rollback and exit if no messages were found IF (@@ROWCOUNT = 0) BEGIN ROLLBACK TRANSACTION; BREAK; END; -- End conversation of end dialog message IF (@message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog') BEGIN PRINT 'End Dialog received for dialog # ' + CAST(@dialog as [nvarchar](40)); END CONVERSATION @dialog; END; ELSE BEGIN SET @EventTime = CAST(CAST(@message_body.query('/EVENT_INSTANCE/PostTime/text()') AS [nvarchar](max)) AS [datetime]) SET @EventType = CAST(@message_body.query('/EVENT_INSTANCE/EventType/text()') AS [nvarchar](128)) INSERT INTO [dbo].[SQLDeadlockEvents] ([EventType], [AlertTime], [DeadlockGraph]) VALUES (@EventType, @EventTime, @message_body) END COMMIT TRANSACTION DECLARE @MailSubject [nvarchar](256) ,@MailBody [nvarchar](max) ,@SQLQuery [nvarchar](max) ,@AttachedFileName [nvarchar](256) ,@EventDateTime [datetime] ,@EventID [int] ,@Counter [int] ,@dbname [sysname] IF EXISTS (SELECT * FROM [tempdb].[dbo].[sysobjects] WHERE name = '##ListDeadlocks' AND type in (N'U')) DROP TABLE ##ListDeadlocks CREATE TABLE ##ListDeadlocks ([EventRowID] [int] ,[AlertTime] [datetime] ,[DeadlockGraph] [xml] ,[DeadlockXml] [xml]) INSERT INTO ##ListDeadlocks ([EventRowID] ,[AlertTime] ,[DeadlockGraph] ,[DeadlockXml]) SELECT [EventRowID] ,[AlertTime] ,[DeadlockGraph].query('./EVENT_INSTANCE/TextData/deadlock-list') ,DeadlockGraph FROM [dbo].[SQLDeadlockEvents] WHERE [EventRowID] = @@IDENTITY select @dbname = db_name(DeadlockGraph.value( '(/deadlock-list//@dbid)[1]', 'int' )) --db_name(DeadlockGraph.value( '(/EVENT_INSTANCE/TextData/deadlock-list//@dbid)[1]', 'int' ) ) FROM ##ListDeadlocks --where [EventRowID] = SCOPE_IDENTITY() update [dbo].[SQLDeadlockEvents] set [DatabaseName] = @dbname where [EventRowID] = (select [EventRowID] FROM ##ListDeadlocks ) SELECT @Counter = MIN(EventRowID) FROM ##ListDeadlocks WHILE @Counter IS NOT NULL BEGIN SELECT @EventDateTime = [AlertTime] ,@EventID = [EventRowID] FROM ##ListDeadlocks WHERE [EventRowID] = @Counter SELECT @MailSubject = 'Um deadlock ocorreu em ' + cast(@dbname as [varchar](256)) --+' servidor: ' --+ CONVERT([varchar](128), SERVERPROPERTY('ServerName')) ,@MailBody = 'Deadlock EventRowID: ' + CAST(@EventID AS [varchar](12)) + char(13) + char(13) + 'O deadlock ocorreu as ' + CONVERT([varchar](50), @EventDateTime, 120) + ' no Servidor: ' + CONVERT([varchar](128), SERVERPROPERTY('ServerName'))+'.'+char(13) + char(13) + 'Verifique o anexo xdl-file para mais detalhes.' + char(13) + char(13) +'Para mais informações ou eventos anteriores consulte: SELECT * FROM BASE_DE_ADMINISTRACAO_DO_DBA.dbo.SQLDeadlockEvents WITH (NOLOCK)' ,@SQLQuery = 'SET NOCOUNT ON; SELECT [DeadlockGraph] FROM [##ListDeadlocks] WITH (READUNCOMMITTED) WHERE EventRowID = ' + CAST(@EventID AS [varchar](10)) ,@AttachedFileName = CONVERT([varchar](128), SERVERPROPERTY('ServerName')) + '_' + CAST(@EventID AS [varchar](12)) + '.xdl' EXEC msdb.dbo.sp_send_dbmail @recipients = 'email@dominio.com.br' -- troque o email aqui ,@subject = @MailSubject ,@body = @MailBody ,@query = @SQLQuery ,@attach_query_result_as_file = 1 ,@query_attachment_filename = @AttachedFileName -- http://support.microsoft.com/kb/924345 ,@query_result_header = 0 ,@query_result_width = 32767 ,@query_no_truncate = 1 DELETE FROM ##ListDeadlocks WHERE EventRowID = @Counter SELECT @Counter = MIN(EventRowID) FROM ##ListDeadlocks end END --End of loop END TRY BEGIN CATCH SELECT ERROR_NUMBER() ,ERROR_SEVERITY() ,ERROR_STATE() ,ISNULL(ERROR_PROCEDURE(), @SQLProcedureName) ,ERROR_LINE() ,ERROR_MESSAGE() END CATCH END GO /*inicia a fila para começar a capturar os DeadLocks*/ ALTER QUEUE [dbo].[DeadlockGraphQueue] WITH STATUS = ON ,ACTIVATION (PROCEDURE_NAME = [usp_sCaptureDeadlockGraph] ,STATUS = ON ,MAX_QUEUE_READERS = 1 ,EXECUTE AS OWNER) GO