Tabelle AsyncOperationBase: Viele Sperren und viele Sätze

Gelegentlich kommt es vor, dass das MSCRM hängt und die Anwender auch nicht benutzten können und asynchrone Jobs sich selber sperren.
Es hilft meist, den „Async Job“ einfach Neustarten…


/*Anzahl der Sätze Total*/
Select Count(1) from [dbo].[AsyncOperationBase] with (nolock)

/*Benutzung*/
Select Sum(Coalesce(Len([Data]),0)) as [Data]
,Sum(Coalesce(Len([WorkflowState]),0)) as [WorkflowState]
,Sum(Coalesce(Len([Message]),0)) as [Message]
,Sum(Coalesce(Len([RegardingObjectIdName]),0)) as [RegardingObjectIdName]
,Sum(Coalesce(Len([RegardingObjectIdYomiName]),0)) as [RegardingObjectIdName]
,Sum(Coalesce(Len([FriendlyMessage]),0)) as [FriendlyMessage]
,Sum(Coalesce(Len([OwningExtensionIdName]),0)) as [OwningExtensionIdName]
from [dbo].[AsyncOperationBase] with (nolock)


/*Interner Zustand der Tabelle... z.B. falls diese riesig ist*/
SELECT 'AsyncOperationBase' Tablename, type_desc, total_pages * 8 as total_pages_in_KB, used_pages * 8 as used_pages_in_KB, total_pages, used_pages, data_pages,container_id, allocation_unit_id 
FROM sys.allocation_units 
WHERE container_id in (SELECT partition_id FROM sys.partitions WHERE OBJECT_ID = OBJECT_ID('AsyncOperationBase') ) 


/*Verteilung bezüglich des StatusCode*/
Select StatusCode, COUNT(1) Anzahl, Case when StatusCode=0 Then 'Ready/WaitingForResources' 
when StatusCode=10 Then 'Suspended/Waiting'
when StatusCode=20 Then 'Locked/InProgress'
when StatusCode=21 Then 'Locked/Pausing'
when StatusCode=22 Then 'Locked/Canceling'
when StatusCode=30 Then 'Completed/Succeeded'
when StatusCode=31 Then 'Completed/Failed'
when StatusCode=32 Then 'Completed/Canceled'
Else 'unbekannter Status'
END StatusCode_Readable
FROM dbo.[AsyncOperationBase] with (nolock)
Group by StatusCode

Wenn es mehrere Millionen Sätze gibt, dann lohnt sich die Missing Indizees einmal anzuschauen…

Es gibt normalerweise eine regelmäßigen Bereinigungs-Job, der alte Sätze löscht.

Sperrende SQL Befehle indentifizieren

/*
Bitte als "sa" anmelden (möglicherweise reicht die Windows-Authentification)
Hinweis: wenn z.B. ein Satz verändert wird, werden neben der Tabelle auch alle Indizes gesperrt.
Das ist der Grund für die vielen gesperrten Zeilen
*/
/*
KILL 141 
KILL 141 WITH STATUSONLY  --dies ist nur bei komplexen Rollback's sinnvoll möglich
*/
USE master
go



-- Locks indentifizieren -------------------------------------------------------------------------------- 
Select request_session_id, blocking_session_id, DatabaseName,dbid, LockedObjectName, LockedObjectId, LockedResource, LockType, LoginName, HostName
	, IsUserTransaction, TransactionName, TransactionStartTime  
    , replace(replace(replace(replace(  ST.text   ,CHAR(9),' ')  ,CHAR(13),' ')  ,CHAR(10),' '),';',':')  AS SqlStatementText /* SQL Statement */

from (
SELECT DISTINCT L.request_session_id AS request_session_id,    -- Anfragender System Process Identifier 
  t2.blocking_session_id as blocking_session_id,	       -- Blockierender System Process Identifier
  --request_owner_guid AS UOW,                        -- Identifiziert die Arbeitseinheits-ID (Unit of Work, UOW) verteilter Transaktionen 
  DB_NAME(L.resource_database_id) AS DatabaseName,  -- Databasename 
  O.Name AS LockedObjectName,                       -- Object name 
  P.object_id AS LockedObjectId,                    -- Eindeutige Object identification number 
  L.resource_type AS LockedResource,                -- Resource which is locked: DATABASE, FILE, OBJECT, PAGE, KEY, EXTENT, RID, APPLICATION, METADATA, HOBT, ALLOCATION_UNIT. 
  L.request_mode AS LockType,                       -- Lock Type: Exclusive locks (X), Intent locks (I), Shared locks (S), Update locks (U),  Schema locks (Sch) 
  --L.resource_description AS LockDescription,      -- LockDescription 
  --ST.text AS SqlStatementText,                      -- SQL Statement 
  --replace(replace(replace(  ST.text   ,CHAR(9),' ')  ,CHAR(13),'/*CR*/')  ,CHAR(10),'/*LF*/')  AS SqlStatementText, /* SQL Statement */
  CN.most_recent_sql_handle,
  ES.login_name AS LoginName,                       -- Loginname mit dem der User an der DB angemeldet ist 
  ES.host_name AS HostName,                         -- Hostname des verbunden clients 
  TST.is_user_transaction as IsUserTransaction,     -- 1 = Die Transaktion wurde von einer Benutzeranforderung initiiert.  0 = Systemtransaktion. 
  AT.name as TransactionName,                       -- Transaktionsname 
  AT.transaction_begin_time as TransactionStartTime,-- Uhrzeit des Transaktionsbeginns. 
  CN.auth_scheme as AuthenticationMethod            -- Login Methode SQL / Windows authentifizierung 

FROM sys.dm_tran_locks L 
  left JOIN sys.partitions P ON P.hobt_id = L.resource_associated_entity_id 
  left JOIN sys.objects O ON O.object_id = P.object_id 
  left JOIN sys.dm_exec_sessions ES ON ES.session_id = L.request_session_id 
  left JOIN sys.dm_tran_session_transactions TST ON ES.session_id = TST.session_id 
  left JOIN sys.dm_tran_active_transactions AT ON TST.transaction_id = AT.transaction_id 
  left JOIN sys.dm_exec_connections CN ON CN.session_id = ES.session_id 
  left  JOIN sys.dm_os_waiting_tasks as t2
        ON L.lock_owner_address = t2.resource_address
) as temp
  CROSS APPLY sys.dm_exec_sql_text(temp.most_recent_sql_handle) AS ST 

WHERE 1=1
      --and resource_database_id = db_id('SalesLogix_Prod')  --nur Locks einer bestimmten Datenbank betrachten
      --and L.request_mode in ('X', 'IX')                    -- Exklusive Locks 
      --and L.request_mode NOT in ('S')
      --and (t2.blocking_session_id IS NOT NULL OR L.request_mode = 'X')  --klare Störer und Opfer darstellen
ORDER BY temp.request_session_id 

MS SQL: Sperren melden als Agentjob

Sperren automatisch erkennen und melden als E-Mail:
 


/*
Erkennen von Sperren und Emails wenn Session dabei warten muessen
- -
Changes:
2015-09-24 11:47 ODFR: Die CSV-Datei kleiner gemacht + den echten Zeitpunkt einer Sperre die behindert! 
2015-09-29 12:34 ODFR: Kommentare sollen nur Slash+Stern sein, sonst Gefahr auf fehlerhafter Funktionalit‰t
2015-09-30 13:22 ODFR: Auch Backup-Jobs m¸ssen ggf. auch dargestellt werden (diese sind keine Transactions)
*/

/*Vorbereitungen:*/
DECLARE @iMinuten int;
SET @iMinuten = 2;
DECLARE @iMinutenSeitSperre int;
DECLARE @cQuery_attachment_filename varchar(64);
SET @cQuery_attachment_filename='Sperren_'+replace(replace(replace(convert(char(16), Getdate(), 121),' ','_'),'-',''),':','')+'.csv';
DECLARE @cQuery_result_separator char(1);
SET @cQuery_result_separator=CHAR(9);


/*Die Query vorbereiten, die sowohl zum Bewerten von Sperren, als auch ggf. in einer Email benutzt wird:*/
DECLARE @cQuery varchar(8000);
SET @cQuery = 'SELECT DISTINCT L.request_session_id AS request_session_id,    /* Anfragender System Process Identifier */
  t2.blocking_session_id as blocking_session_id,    /*Blockierender System Process Identifier */
  /*request_owner_guid AS UOW,*/                      /* Identifiziert die Arbeitseinheits-ID (Unit of Work, UOW) verteilter Transaktionen */
  DB_NAME(L.resource_database_id) AS DatabaseName,  /* Databasename */
  O.Name AS LockedObjectName,                       /* Object name */
  P.object_id AS LockedObjectId,                    /* Eindeutige Object identification number */
  L.resource_type AS LockedResource,                /* Resource which is locked: DATABASE, FILE, OBJECT, PAGE, KEY, EXTENT, RID, APPLICATION, METADATA, HOBT, ALLOCATION_UNIT. */
  L.request_mode AS LockType,                       /* Lock Type: Exclusive locks (X), Intent locks (I), Shared locks (S), Update locks (U),  Schema locks (Sch) */
  /*L.resource_description AS LockDescription,*/      /* LockDescription */
  replace(replace(replace(  ST.text   ,CHAR(9),'' '')  ,CHAR(13),''/*CR*/'')  ,CHAR(10),''/LF*/'')  AS SqlStatementText,                      /* SQL Statement */
  ES.login_name AS LoginName,                       /* Loginname mit dem der User an der DB angemeldet ist */
  ES.host_name AS HostName,                         /* Hostname des verbunden clients */
  TST.is_user_transaction as IsUserTransaction,     /* 1 = Die Transaktion wurde von einer Benutzeranforderung initiiert.  0 = Systemtransaktion. */
  AT.name as TransactionName,                       /* Transaktionsname */
  AT.transaction_begin_time as TransactionStartTime,/* Uhrzeit des Transaktionsbeginns.  */
  coalesce(AT.transaction_begin_time, ES.last_request_start_time) as SQLJobStartTime, /* Uhrzeit des Beginns des laufenden SQLs.  */
  CN.auth_scheme as AuthenticationMethod            /* Login Methode SQL / Windows Authentifizierung */
FROM sys.dm_tran_locks L 
  left JOIN sys.partitions P ON P.hobt_id = L.resource_associated_entity_id 
  left JOIN sys.objects O ON O.object_id = P.object_id 
  left JOIN sys.dm_exec_sessions ES ON ES.session_id = L.request_session_id 
  left JOIN sys.dm_tran_session_transactions TST ON ES.session_id = TST.session_id 
  left JOIN sys.dm_tran_active_transactions AT ON TST.transaction_id = AT.transaction_id 
  left JOIN sys.dm_exec_connections CN ON CN.session_id = ES.session_id 
  CROSS APPLY sys.dm_exec_sql_text(CN.most_recent_sql_handle) AS ST 
  left  JOIN sys.dm_os_waiting_tasks as t2
        ON L.lock_owner_address = t2.resource_address
WHERE 1=1
      /*and resource_database_id = db_id(''MYDATABASE'')*/  /*nur Locks einer bestimmten Datenbank betrachten*/
      /*and L.request_mode in (''X'', ''IX'')     */               /* nur Exklusive Locks */
      and (t2.blocking_session_id IS NOT NULL OR L.request_mode like ''%X%'' or L.request_mode = ''NULL''  or L.request_mode is NULL)  /*mˆgliche Stˆrer und klare Opfer darstellen*/
ORDER BY L.request_session_id';


/*Code zum Bewerten von Sperren:*/
Declare @dTransactionStartTime Datetime;
Declare @tTable Table (
request_session_id int,    /* Anfragender System Process Identifier */
blocking_session_id int,    /*Blockierender System Process Identifier */
DatabaseName nvarchar(255),  /* Databasename */
LockedObjectName nvarchar(255),                       /* Object name */
LockedObjectId int,                    /* Eindeutige Object identification number */
LockedResource nvarchar(255),          /* Resource which is locked: DATABASE, FILE, OBJECT, PAGE, KEY, EXTENT, RID, APPLICATION, METADATA, HOBT, ALLOCATION_UNIT. */
LockType nvarchar(10),                       /* Lock Type: Exclusive locks (X), Intent locks (I), Shared locks (S), Update locks (U),  Schema locks (Sch) */
SqlStatementText nvarchar(max),                      /* SQL Statement */
LoginName nvarchar(255),                       /* Loginname mit dem der User an der DB angemeldet ist */
HostName nvarchar(255),                         /* Hostname des verbunden clients */
IsUserTransaction int,     /* 1 = Die Transaktion wurde von einer Benutzeranforderung initiiert.  0 = Systemtransaktion. */
TransactionName nvarchar(255),                       /* Transaktionsname */
TransactionStartTime datetime,/* Uhrzeit des Transaktionsbeginns.  */
SQLJobStartTime datetime,/* Uhrzeit des Transaktionsbeginns.  */
AuthenticationMethod nvarchar(255)           /* Login Methode SQL / Windows Authentifizierung */
)
/*Einmal die Eckwerte aller aktuellen Session einlesen, die entweder sperren oder selber warten muessen*/
Insert into @tTable
exec ( @cQuery )
/* Aelteste Sperre, egal ob ein echtes Warten vorliegt oder nicht */
/*
SELECT TOP 1 @dTransactionStartTime=AT.transaction_begin_time
FROM sys.dm_tran_locks L 
  left JOIN sys.partitions P ON P.hobt_id = L.resource_associated_entity_id 
  left JOIN sys.objects O ON O.object_id = P.object_id 
  left JOIN sys.dm_exec_sessions ES ON ES.session_id = L.request_session_id 
  left JOIN sys.dm_tran_session_transactions TST ON ES.session_id = TST.session_id 
  left JOIN sys.dm_tran_active_transactions AT ON TST.transaction_id = AT.transaction_id 
WHERE AT.transaction_begin_time IS NOT NULL
      and L.request_mode NOT in ('S','IS')
ORDER BY AT.transaction_begin_time ASC;
*/
/* Nur Sperren beachten, wenn ein andere Session wartet */
/*
Select @dTransactionStartTime=Min(AT.transaction_begin_time)
FROM sys.dm_tran_locks L 
  left JOIN sys.dm_exec_sessions ES ON ES.session_id = L.request_session_id 
  left JOIN sys.dm_tran_session_transactions TST ON ES.session_id = TST.session_id 
  left JOIN sys.dm_tran_active_transactions AT ON TST.transaction_id = AT.transaction_id 
  left  JOIN sys.dm_os_waiting_tasks as t2 ON L.lock_owner_address = t2.resource_address
having count(t2.blocking_session_id) > 0
*/
/*Tabelle definieren nur für sperrende und abhaengige Sessions:*/
Declare @tTable2 Table (
request_session_id int,    /* Anfragender System Process Identifier */
blocking_session_id int,    /*Blockierender System Process Identifier */
TransactionStartTime datetime /* Uhrzeit des Transaktionsbeginns.  */
)
/*Und nun die sperrende und abhaengige Sessions aus vorhin befuellten Tabelle bestimmen:*/
;With hLocks (request_session_id, blocking_session_id, TransactionStartTime)
as (
	Select request_session_id, blocking_session_id, SQLJobStartTime
	From @tTable t1 
	where blocking_session_id is NOT NULL
	union all
	Select t2.request_session_id, t2.blocking_session_id, t2.SQLJobStartTime 
	from (
		Select request_session_id, blocking_session_id, SQLJobStartTime
		From @tTable 
		) t2 inner join hLocks on t2.request_session_id = hLocks.blocking_session_id
)
Insert into @tTable2
Select * from hLocks
Select @dTransactionStartTime = Min(TransactionStartTime) from @tTable2
/*
Select * from @tTable
Select @dTransactionStartTime
*/


/*Nun die Ergbnisse bewerten:*/
SET @iMinutenSeitSperre=coalesce(datediff(mi,@dTransactionStartTime,GetDate()),0);

DECLARE @cSubject varchar(128);
SET @cSubject = 'SPERRE SEIT MEHR ALS '+cast(@iMinutenSeitSperre as varchar(10))+' Minuten';

DECLARE @cBody varchar(512);
SET @cBody = 'Die Sperre fing an um '+convert(char(16), @dTransactionStartTime, 121)+' und somit vor '+cast(@iMinutenSeitSperre as varchar(10))+' Minuten.'
	+CHAR(13)+'Es könnte in '+cast((60 - (@iMinutenSeitSperre % 60) ) as varchar(10))+' Minuten eine neue Email versendet werden,'
	+CHAR(13)+'wenn das Problem bis dahin nicht gelöst ist!';

/*Die erste Email darf natürlich kommen und erst dann nur alle volle Stunden*/
/*es können derzeit (leider) bis 3 Email versendet werden */
If @iMinutenSeitSperre > 0
	If (@iMinutenSeitSperre % 60) between @iMinuten and (@iMinuten * 2)
	BEGIN
		/*print 'SPERRE SEIT MEHR ALS '+cast(@iMinuten as varchar(10))+' Minuten'*/
		EXEC msdb.dbo.sp_send_dbmail
		@profile_name = 'SQLService'
		,@recipients = 'admins@emailserver.local'
	    	,@copy_recipients = 'user1@emailserver.local'
		/*,@blind_copy_recipients = 'private1@emailserver.local'*/
	    	,@subject = @cSubject
		,@body = @cBody
		,@query = @cQuery
		,@query_result_header = 1
		,@query_result_separator = @cQuery_result_separator
		,@query_no_truncate = 1
		,@append_query_error = 1
		,@query_result_width = 32767
		,@query_attachment_filename = @cQuery_attachment_filename  /*'Sperren.csv'*/
		,@attach_query_result_as_file = 1 ;
	END
/*
	ELSE
	BEGIN
		print @cBody;
	END
Else
	print 'Keine Sperren.'	
*/

Voraussetzungen: Database Mail ist aktiviert und eingerichtet. Es muss das „Profil“ und natürlich die Ziel Emailadressen angepasst werden.

Ebenso kann es sein, dass im Database Mail die Beschränkung der Attachment-Größe etwas gelockert wird (Systemparameter in Database Mail).

Link: https://docs.microsoft.com/de-de/sql/relational-databases/database-mail/configure-database-mail#SystemParameters