Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
| Author |
Topic |
|
coolerbob
Aged Yak Warrior
841 Posts |
Posted - 2008-04-23 : 11:29:32
|
| I would like to log blocks to see what causes it.One hack of a way to do it is to run code similar to that which populates the blk column in sp_who; on a job every 5 seconds. It would store the results of code similar to the sp_who output whenever any of the rows has a value greater than zero in the blk column. This way, I can see what was going on at the time there was a problem.Does anyone have a better strategy for this? |
|
|
sodeep
Master Smack Fu Yak Hacker
7174 Posts |
Posted - 2008-04-23 : 13:12:32
|
| 1)query sysprocesses system table2) Use DBCC inputbuffer(spid) to find what statements caused it3) mail it to DBA using DBmail by creating jobs |
 |
|
|
rmiao
Master Smack Fu Yak Hacker
7266 Posts |
Posted - 2008-04-23 : 22:59:26
|
| Does sysprocesses have command column? |
 |
|
|
sodeep
Master Smack Fu Yak Hacker
7174 Posts |
Posted - 2008-04-24 : 10:08:21
|
| They do have. |
 |
|
|
coolerbob
Aged Yak Warrior
841 Posts |
Posted - 2008-04-24 : 10:48:22
|
| 3 cheers for sodeep! i like it.I have a sproc I'm going to call from a job to do the logging I described.Just have one challenge left. How do you turn the results of a DBCC command into a table you can select from?Here's the problem:declare @instanceID intset @instanceID=(select max(BLK_InstanceID)+1 from admin.dbo.MonitorBlocks)--1) Log participating records to the tableinsert MonitorBlocks (BLK_Spid,BLK_BlockBySpid,BLK_HostName,BLK_ProgramName,BLK_CommandType,BLK_CommandText,BLK_InstanceID,BLK_CreateDate)select spid,blocked,hostname,[program_name],cmd,'',@instanceID,getdate()from master.dbo.sysprocesses pwhere BLK_BlockBySpid<>0 or exists (select * from master.dbo.sysprocesses a where a.spid>0 and a.spid=BLK_BlockBySpid)--2) Update CmdTextdeclare @nextSpid intdeclare @cmdText varchar(255)while (select count(*) from MonitorBlocks where BLK_InstanceID=@instanceID and BLK_CommandText='')>0begin set @nextSpid=(select top 1 BLK_Spid from MonitorBlocks where BLK_InstanceID=@instanceID and BLK_CommandText='') set @cmdText=(select EventInfo from DBCC inputbuffer(@nextSpid) ) update MonitorBlocks set BLK_CommandText=@cmdText where BLK_InstanceID=@instanceID and BLK_Spid=@nextSpidend |
 |
|
|
coolerbob
Aged Yak Warrior
841 Posts |
Posted - 2008-04-24 : 11:36:01
|
| And if you want to create the table first:use [admin]BEGIN TRANSACTIONSET QUOTED_IDENTIFIER ONSET ARITHABORT ONSET NUMERIC_ROUNDABORT OFFSET CONCAT_NULL_YIELDS_NULL ONSET ANSI_NULLS ONSET ANSI_PADDING ONSET ANSI_WARNINGS ONCOMMITBEGIN TRANSACTIONGOCREATE TABLE dbo.MonitorBlocks ( BLK_ID int NOT NULL IDENTITY (1, 1), BLK_Spid int NOT NULL, BLK_BlockBySpid int NOT NULL, BLK_HostName varchar(50) NOT NULL, BLK_ProgramName varchar(50) NOT NULL, BLK_CommandType varchar(50) NOT NULL, BLK_CommandText varchar(50) NOT NULL, BLK_InstanceID int NOT NULL ) ON [PRIMARY]GOALTER TABLE dbo.MonitorBlocks ADD CONSTRAINT DF_MonitorBlocks_BLK_BlockBySpid DEFAULT 0 FOR BLK_BlockBySpidGOALTER TABLE dbo.MonitorBlocks ADD CONSTRAINT DF_MonitorBlocks_BLK_HostName DEFAULT '' FOR BLK_HostNameGOALTER TABLE dbo.MonitorBlocks ADD CONSTRAINT DF_MonitorBlocks_BLK_ProgramName DEFAULT '' FOR BLK_ProgramNameGOALTER TABLE dbo.MonitorBlocks ADD CONSTRAINT DF_MonitorBlocks_BLK_CommandType DEFAULT '' FOR BLK_CommandTypeGOALTER TABLE dbo.MonitorBlocks ADD CONSTRAINT DF_MonitorBlocks_BLK_CommandText DEFAULT '' FOR BLK_CommandTextGOALTER TABLE dbo.MonitorBlocks ADD CONSTRAINT DF_MonitorBlocks_BLK_InstanceID DEFAULT 0 FOR BLK_InstanceIDGOALTER TABLE dbo.MonitorBlocks ADD CONSTRAINT PK_MonitorBlocks PRIMARY KEY CLUSTERED ( BLK_ID ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]GOCREATE NONCLUSTERED INDEX IX_MonitorBlocks_InstanceID ON dbo.MonitorBlocks ( BLK_InstanceID DESC ) WITH( PAD_INDEX = ON, FILLFACTOR = 99, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]GOCOMMITBEGIN TRANSACTIONSET QUOTED_IDENTIFIER ONSET ARITHABORT ONSET NUMERIC_ROUNDABORT OFFSET CONCAT_NULL_YIELDS_NULL ONSET ANSI_NULLS ONSET ANSI_PADDING ONSET ANSI_WARNINGS ONCOMMITBEGIN TRANSACTIONGOALTER TABLE dbo.MonitorBlocks ADD BLK_CreateDate datetime NOT NULL CONSTRAINT DF_MonitorBlocks_BLK_CreateDate DEFAULT getdate()GOCOMMIT |
 |
|
|
sodeep
Master Smack Fu Yak Hacker
7174 Posts |
Posted - 2008-04-24 : 11:59:28
|
| Here is what you need:declare @spid int, @counter intcreate table ##temp_blocked_spids (id int identity (1,1) primary key, spid int, blocked int, hostname nchar(256), program_name nchar(256), loginame nchar(256))create table ##ib_info (id int identity (1,1) primary key, spid int, eventtype varchar(1000), parameters varchar(1000), inputbuffer varchar(2000))insert into ##temp_blocked_spids (spid, blocked, hostname, program_name, loginame)select spid, blocked, rtrim(hostname), rtrim(program_name), rtrim(loginame)from sysprocesseswhere (blocked != 0 and spid !=blocked) or (spid in (select distinct blocked from sysprocesses where blocked !=0 and spid !=blocked))group by spid, blocked, hostname, program_name, loginameorder by spidset nocount ondeclare ibuffer cursor fast_forward forselect spid from ##temp_blocked_spidsopen ibuffer fetch next from ibuffer into @spidwhile (@@fetch_status != -1) begin insert into ##ib_info (eventtype, parameters, inputbuffer) exec ('dbcc inputbuffer (' + @spid + ')') fetch next from ibuffer into @spid enddeallocate ibufferset nocount off-- If there are entries in the table, send an email.if (select count(*) from ##temp_blocked_spids) > 0 begin EXEC msdb.dbo.sp_send_dbmail @profile_name = 'name', @recipients = 'email', @query = 'select left(s.spid,4) as spid, left(s.blocked,4) as blocked, left(d.inputbuffer,52) as inputbuffer, left(s.program_name,10) as program_name, left(s.loginame,10) as login_name,left(s.hostname,15) as host_name from ##temp_blocked_spids s join ##ib_info d on (s.id = d.id)', @body = 'The following spids are involved in blocking', @subject = 'Blocking on ---' ; end-- Drop the temporary tables.drop table ##temp_blocked_spidsdrop table ##ib_info |
 |
|
|
coolerbob
Aged Yak Warrior
841 Posts |
Posted - 2008-04-24 : 12:22:55
|
| insert into ##ib_info (eventtype, parameters, inputbuffer)exec ('dbcc inputbuffer (' + @spid + ')')That's what I was afterthanks!Here's the finished product:declare @instanceID intset @instanceID=(select isnull(max(BLK_InstanceID)+1,0) from admin.dbo.MonitorBlocks)declare @table table (eventtype varchar(50), parameters varchar(50), EventInfo varchar(50))--1) Log participating records to the tableinsert MonitorBlocks (BLK_Spid,BLK_BlockBySpid,BLK_HostName,BLK_ProgramName,BLK_CommandType,BLK_CommandText,BLK_InstanceID,BLK_CreateDate)select spid,blocked,hostname,[program_name],cmd,'',@instanceID,getdate()from master.dbo.sysprocesses pwhere blocked<>0 or exists (select * from master.dbo.sysprocesses a where a.spid>0 and p.spid=a.blocked)--2) Update CmdTextdeclare @nextSpid intdeclare @cmdText varchar(255)while (select count(*) from MonitorBlocks where BLK_InstanceID=@instanceID and BLK_CommandText='' and BLK_Processed=0)>0begin set @nextSpid=(select top 1 BLK_Spid from MonitorBlocks where BLK_InstanceID=@instanceID and BLK_CommandText='' and BLK_Processed=0) delete from @table insert into @table (eventtype, parameters, EventInfo) exec ('dbcc inputbuffer (' + @nextSpid + ')') set @cmdText=(select top 1 EventInfo from @table ) update MonitorBlocks set BLK_CommandText=isnull(@cmdText,''), BLK_Processed=1 where BLK_InstanceID=@instanceID and BLK_Spid=@nextSpidend |
 |
|
|
Ola Hallengren
Starting Member
33 Posts |
|
|
coolerbob
Aged Yak Warrior
841 Posts |
Posted - 2008-04-28 : 03:51:40
|
| Wow! Thanks Ola! What I wrote is working quite well, but it's nice to see something more "official". I'll implement this too and compare the results. Thanks again for posting this. This is the level of replies that makes this site so good. |
 |
|
|
jen
Master Smack Fu Yak Hacker
4110 Posts |
Posted - 2008-04-28 : 05:39:13
|
instead of dbcc inputbuffer.. another alternative isi didn't post the table schema, just check out which information you want to save...CREATE procedure [dbo].[GetBlocks] @dbname nvarchar(60)=null as set nocount on declare @datestamp datetime set @datestamp=getdate() insert into blocks(datestamp,spid,blockingspid,cpu,physical_io,mempages,logintime,login_name ,waittimeinsec,lastwaittype,waitresource,numofopentran,currentcommand,sqlstatement,blockingcommand ,blockingstatement,lockedobject ,lastbatch) select @datestamp,spid.spid,spid.blocked,spid.cpu,spid.physical_io,spid.memusage as [Mem_Pages] ,spid.login_time,spid.loginame ,spid.waittime/1000 as [WaitTime(s)] ,spid.lastwaittype ,spid.waitresource ,spid.open_tran ,spid.cmd [CurrentCommand] ,(select text from sys.dm_exec_sql_text(spid.sql_handle)) as sqlstatement ,case when blockingspid.cmd='Awaiting Command' then 'OpenTran Waiting for Commit' else blockingspid.cmd end as [BlockingCommand] ,(select text from sys.dm_exec_sql_text(blockingspid.sql_handle)) as blockingstatement ,locks.resource_type + ':' + convert(varchar(100),locks.resource_associated_entity_id) + ':' + locks.request_status + ':' + locks.request_mode as [ResourceType:LockedTable:Status:Mode] ,spid.last_batch from master..sysprocesses SPID join master..sysprocesses BlockingSPID on spid.blocked=blockingspid.spid join sys.dm_tran_locks locks on locks.request_session_id=spid.spid where (spid.dbid=coalesce(db_id(@dbname),spid.dbid) and spid.dbid not in (select database_id from sys.databases where name in ('master','model','tempdb','msdb')) ) and spid.spid<>@@spid and spid.spid<>blockingspid.spid and spid.blocked>0 and locks.request_mode not in ('S','Sch-S') and not exists (select * from srvadmin..blocks where spid=spid.spid and currentcommand=spid.cmd and NumOfOpenTran=spid.open_tran and lastbatch=spid.last_batch) --------------------keeping it simple... |
 |
|
|
coolerbob
Aged Yak Warrior
841 Posts |
Posted - 2008-04-30 : 05:49:58
|
| Thanks Jen,I've not had much luck getting sensible results from dbcc inputbuffer. It's been spurious.So I'll try this too along with Ola's approach and let you know how I get on.What's this though?srvadmin..blocks |
 |
|
|
coolerbob
Aged Yak Warrior
841 Posts |
Posted - 2008-05-01 : 11:59:18
|
What do I put where the question mark is? I dont know the exact event name for this:CREATE EVENT NOTIFICATION DeadlockNotificationON SERVERFOR ?TO SERVICE 'NotifyService', '8140a771-3c4b-4479-8ac0-81008ab17984' ;quote: Originally posted by Ola Hallengren I think that you should have a look at the 'Blocked Process Report' events.You can define that you would like to generate a 'Blocked Process Report' if a process is blocked for more than a number of seconds.The threshold is set with a sp_configure option.http://msdn2.microsoft.com/en-us/library/ms181150.aspxThe 'Blocked Process Report' contains information about the blocked and blocking process.http://msdn2.microsoft.com/en-us/library/ms191168.aspxThen you could capture these events using SQL Trace or Event Notifications. If you're using Event Notifications you could have the events inserted into a table.http://msdn2.microsoft.com/en-us/library/ms190427.aspxOla Hallengrenhttp://ola.hallengren.com
|
 |
|
|
Ola Hallengren
Starting Member
33 Posts |
Posted - 2008-05-01 : 12:19:07
|
| You should have a look at the BLOCKED_PROCESS_REPORT and DEADLOCK_GRAPH trace event types. You could put both in the same event notification with a comma in between.http://msdn.microsoft.com/en-us/library/ms187476.aspxOla Hallengrenhttp://ola.hallengren.com |
 |
|
|
coolerbob
Aged Yak Warrior
841 Posts |
Posted - 2008-05-02 : 04:39:32
|
| Brilliant, thanks.Two other questions:When I tried calling this, I gave up after 90 seconds. Any reason you know of why it should take so long?ALTER DATABASE MyDBSET ENABLE_BROKER;GO(It's in this sample C:\Program Files\Microsoft SQL Server\90\Samples\Engine\Administration\EventNotifications\Scripts\)2nd, you say I could log the results to a table. What would that sql look like?Would you put a INSERT statement somewhere in here like this? (I would test this myself if it wasn't for the set ENABLE_BROKER problem I'm having):select * into NewTable fromWAITFOR ( RECEIVE CASE WHEN validation = 'X' THEN CAST(message_body as XML) ELSE NULL END AS message_body ,* FROM [AdventureWorks].[dbo].[Notify-Queue]), TIMEOUT 10000 |
 |
|
|
TRACEYSQL
Aged Yak Warrior
594 Posts |
|
|
Ola Hallengren
Starting Member
33 Posts |
Posted - 2008-05-03 : 08:22:22
|
| Here is some code that I'm using.-- First make sure that service broker is enabled in the current database.SELECT [name], is_broker_enabled FROM sys.databases-- Create an audit tableCREATE TABLE dbo.Audit ( AuditID int NOT NULL IDENTITY, PostTime datetime NULL, EventType sysname NULL, LoginName sysname NULL, UserName sysname NULL, ServerName sysname NULL, DatabaseName sysname NULL, ObjectType sysname NULL, SchemaName sysname NULL, ObjectName sysname NULL, EventData xml NULL,CONSTRAINT PK_Audit PRIMARY KEY CLUSTERED (AuditID ASC))GOCREATE PROCEDURE dbo.AuditReceiveASBEGIN TRYDECLARE @AuditMessage TABLE (EventData xml)BEGIN TRANSACTION;RECEIVE CAST(message_body AS XML)FROM dbo.AuditQueueINTO @AuditMessageINSERT INTO dbo.Audit (PostTime, EventType, LoginName, UserName, ServerName, DatabaseName, ObjectType, SchemaName, ObjectName, EventData)SELECT CAST(EventData.query('data(//PostTime)') AS varchar(23)), NULLIF(CAST(EventData.query('data(//EventType)') AS sysname),''), NULLIF(CAST(EventData.query('data(//LoginName)') AS sysname),''), NULLIF(CAST(EventData.query('data(//UserName)') AS sysname),''), NULLIF(CAST(EventData.query('data(//ServerName)') AS sysname),''), NULLIF(CAST(EventData.query('data(//DatabaseName)') AS sysname),''), NULLIF(CAST(EventData.query('data(//ObjectType)') AS sysname),''), NULLIF(CAST(EventData.query('data(//SchemaName)') AS sysname),''), NULLIF(CAST(EventData.query('data(//ObjectName)') AS sysname),''), EventData FROM @AuditMessageCOMMIT TRANSACTIONEND TRYBEGIN CATCHROLLBACK TRANSACTIONEND CATCHGO-- Create a queue to receive messagesCREATE QUEUE dbo.AuditQueueWITH STATUS = ON, ACTIVATION (STATUS = ON, PROCEDURE_NAME = dbo.AuditReceive, MAX_QUEUE_READERS = 1, EXECUTE AS Owner)GO-- Create a service on the queue that references the event notifications contractCREATE SERVICE AuditServiceON QUEUE dbo.AuditQueue ([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification])GO-- Create a route on the service to define the address to which Service Broker sends messages for the serviceCREATE ROUTE AuditRouteWITH SERVICE_NAME = 'AuditService', ADDRESS = 'LOCAL'GO-- Create the event notificationCREATE EVENT NOTIFICATION AuditEventNotificationON SERVER FORDDL_TABLE_EVENTSTO SERVICE 'AuditService', 'current database' |
 |
|
|
Ola Hallengren
Starting Member
33 Posts |
Posted - 2008-05-03 : 08:26:59
|
| >When I tried calling this, I gave up after 90 seconds. Any reason you know of why it should take so long?I think that there should be no other connections against the database, when you execute the command. This should be an instant operation. |
 |
|
|
|
|
|
|
|