Sql-Server

阻塞程序報告中的空阻塞程序

  • February 16, 2022

我正在使用擴展事件收集被阻止的程序報告,並且由於某種原因在某些報告中blocking-process節點是空的。這是完整的xml:

<blocked-process-report monitorLoop="383674">
<blocked-process>
 <process id="processa7bd5b868" taskpriority="0" logused="106108620" waitresource="KEY: 6:72057613454278656 (8a2f7bc2cd41)" waittime="25343" ownerId="1051989016" transactionname="user_transaction" lasttranstarted="2017-03-20T09:30:38.657" XDES="0x21f382d9c8" lockMode="X" schedulerid="7" kpid="15316" status="suspended" spid="252" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-03-20T09:39:15.853" lastbatchcompleted="2017-03-20T09:39:15.850" lastattention="1900-01-01T00:00:00.850" clientapp="Microsoft Dynamics AX" hostname="***" hostpid="1348" loginname="***" isolationlevel="read committed (2)" xactid="1051989016" currentdb="6" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
  <executionStack>
   <frame line="1" stmtstart="40" sqlhandle="0x02000000f7def225b0edaecd8744b453ce09bdcff9b291f50000000000000000000000000000000000000000" />
   <frame line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" />
  </executionStack>
  <inputbuf>
(@P1 bigint,@P2 int)DELETE FROM DIMENSIONFOCUSUNPROCESSEDTRANSACTIONS WHERE ((PARTITION=5637144576) AND ((FOCUSDIMENSIONHIERARCHY=@P1) AND (STATE=@P2)))   </inputbuf>
 </process>
</blocked-process>
<blocking-process>
 <process />
</blocking-process>
</blocked-process-report>

這個 hobt_id 所屬索引的索引定義是

CREATE UNIQUE CLUSTERED INDEX [I_7402FOCUSDIMENSIONHIERARCHYIDX] ON [dbo].[DIMENSIONFOCUSUNPROCESSEDTRANSACTIONS]
(
   [PARTITION] ASC,
   [FOCUSDIMENSIONHIERARCHY] ASC,
   [STATE] ASC,
   [GENERALJOURNALENTRY] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

不涉及分區,這是表定義:

CREATE TABLE [dbo].[DIMENSIONFOCUSUNPROCESSEDTRANSACTIONS](
   [FOCUSDIMENSIONHIERARCHY] [bigint] NOT NULL DEFAULT ((0)),
   [GENERALJOURNALENTRY] [bigint] NOT NULL DEFAULT ((0)),
   [STATE] [int] NOT NULL DEFAULT ((0)),
   [RECVERSION] [int] NOT NULL DEFAULT ((1)),
   [PARTITION] [bigint] NOT NULL DEFAULT ((5637144576.)),
   [RECID] [bigint] NOT NULL,
CONSTRAINT [I_7402RECID] PRIMARY KEY NONCLUSTERED 
(
   [RECID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[DIMENSIONFOCUSUNPROCESSEDTRANSACTIONS]  WITH CHECK ADD CHECK  (([RECID]<>(0)))
GO

整個數據庫中的任何表上都沒有定義觸發器或外鍵。

確切的 SQL Server 版本是:

Microsoft SQL Server 2012 (SP3-CU4) (KB3165264) - 11.0.6540.0 (X64)

2016 年 6 月 23 日 17:45:11 版權所有 (c) Microsoft Corporation Enterprise Edition:Windows NT 6.3 上基於核心的許可(64 位)(內部版本 14393:)(管理程序)

擴展事件相當簡單,只記錄阻塞程序報告:

CREATE EVENT SESSION [Dynperf_Blocking_Data] ON SERVER 
ADD EVENT sqlserver.blocked_process_report(
   ACTION(package0.collect_system_time,sqlserver.client_hostname,sqlserver.context_info)),
ADD EVENT sqlserver.lock_escalation(
   ACTION(package0.collect_system_time,sqlserver.client_hostname,sqlserver.context_info)),
ADD EVENT sqlserver.xml_deadlock_report(
   ACTION(package0.collect_system_time,sqlserver.client_hostname,sqlserver.context_info)) 
ADD TARGET package0.event_file(SET filename=N'F:\SQLTrace\Dynamics_Blocking.xel',max_file_size=(100),max_rollover_files=(10))
WITH (MAX_MEMORY=32768 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=PER_NODE,TRACK_CAUSALITY=ON,STARTUP_STATE=ON)
GO

數據庫在 Read Committed Snapshot Isolation 中配置,最大並行度設置為 1。這是伺服器配置:

+------------------------------------+-------+
|                name                | value |
+------------------------------------+-------+
| access check cache bucket count    |     0 |
| access check cache quota           |     0 |
| Ad Hoc Distributed Queries         |     0 |
| affinity I/O mask                  |     0 |
| affinity mask                      |     0 |
| affinity64 I/O mask                |     0 |
| affinity64 mask                    |     0 |
| Agent XPs                          |     1 |
| allow updates                      |     0 |
| backup compression default         |     1 |
| blocked process threshold (s)      |     2 |
| c2 audit mode                      |     0 |
| clr enabled                        |     0 |
| common criteria compliance enabled |     0 |
| contained database authentication  |     0 |
| cost threshold for parallelism     |     5 |
| cross db ownership chaining        |     0 |
| cursor threshold                   |    -1 |
| Database Mail XPs                  |     1 |
| default full-text language         |  1033 |
| default language                   |     0 |
| default trace enabled              |     1 |
| disallow results from triggers     |     0 |
| EKM provider enabled               |     0 |
| filestream access level            |     0 |
| fill factor (%)                    |     0 |
| ft crawl bandwidth (max)           |   100 |
| ft crawl bandwidth (min)           |     0 |
| ft notify bandwidth (max)          |   100 |
| ft notify bandwidth (min)          |     0 |
| index create memory (KB)           |     0 |
| in-doubt xact resolution           |     0 |
| lightweight pooling                |     0 |
| locks                              |     0 |
| max degree of parallelism          |     1 |
| max full-text crawl range          |     4 |
| max server memory (MB)             | 65536 |
| max text repl size (B)             | 65536 |
| max worker threads                 |     0 |
| media retention                    |     0 |
| min memory per query (KB)          |  1024 |
| min server memory (MB)             |     0 |
| nested triggers                    |     1 |
| network packet size (B)            |  4096 |
| Ole Automation Procedures          |     0 |
| open objects                       |     0 |
| optimize for ad hoc workloads      |     1 |
| PH timeout (s)                     |    60 |
| precompute rank                    |     0 |
| priority boost                     |     0 |
| query governor cost limit          |     0 |
| query wait (s)                     |    -1 |
| recovery interval (min)            |     0 |
| remote access                      |     1 |
| remote admin connections           |     0 |
| remote login timeout (s)           |    10 |
| remote proc trans                  |     0 |
| remote query timeout (s)           |   600 |
| Replication XPs                    |     0 |
| scan for startup procs             |     1 |
| server trigger recursion           |     1 |
| set working set size               |     0 |
| show advanced options              |     1 |
| SMO and DMO XPs                    |     1 |
| transform noise words              |     0 |
| two digit year cutoff              |  2049 |
| user connections                   |     0 |
| user options                       |     0 |
| xp_cmdshell                        |     0 |
+------------------------------------+-------+

我執行了一段時間的伺服器端跟踪,並在跟踪文件中獲得了與使用擴展事件相同的空節點。

此阻塞程序報告是使用另一台同樣執行 Dynamics AX 的伺服器上的伺服器端跟踪擷取的,因此它不是特定於該伺服器或內部版本。

<blocked-process-report monitorLoop="1327922">
<blocked-process>
 <process id="processbd9839848" taskpriority="0" logused="1044668" waitresource="KEY: 5:72057597098328064 (1d7966fe609a)" waittime="316928" ownerId="3415555263" transactionname="user_transaction" lasttranstarted="2017-03-27T07:59:29.290" XDES="0x1c1c0c3b0" lockMode="U" schedulerid="3" kpid="25236" status="suspended" spid="165" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-03-27T07:59:47.873" lastbatchcompleted="2017-03-27T07:59:47.873" lastattention="2017-03-27T07:58:01.490" clientapp="Microsoft Dynamics AX" hostname="***" hostpid="11072" loginname="***" isolationlevel="read committed (2)" xactid="3415555263" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
  <executionStack>
   <frame line="1" stmtstart="236" stmtend="676" sqlhandle="0x020000004d6830193d42a167edd195c201f40bb772e9ece20000000000000000000000000000000000000000"/>
  </executionStack>
  <inputbuf>
(@P1 numeric(32,16),@P2 int,@P3 bigint,@P4 nvarchar(5),@P5 nvarchar(36),@P6 int,@P7 numeric(32,16),@P8 bigint,@P9 int)UPDATE PRODCALCTRANS SET REALCOSTAMOUNT=@P1,RECVERSION=@P2 WHERE (((((((PARTITION=@P3) AND (DATAAREAID=@P4)) AND (COLLECTREFPRODID=@P5)) AND (COLLECTREFLEVEL=@P6)) AND (LINENUM=@P7)) AND (RECID=@P8)) AND (RECVERSION=@P9))   </inputbuf>
 </process>
</blocked-process>
<blocking-process>
 <process/>
</blocking-process>
</blocked-process-report>

有人對這些報導有解釋嗎?是什麼阻止了查詢?

如果我在鎖早已消失後查看報告,有什麼方法可以查明發生了什麼?

添加可能有用的一件事是這些查詢是通過sp_cursorpreparesp_cursorexecute

到目前為止,我還無法重現它,它似乎是隨機發生的,但經常發生。

它發生在幾個實例(不同版本)和幾個表/查詢上,都與 Dynamics AX 相關。

當時沒有在後台發生索引或其他數據庫維護作業。

使用srutzky 在答案中提供的程式碼,我能夠擷取與此阻塞程序報告相關的一些日誌記錄:

<blocked-process-report monitorLoop="1621637">
<blocked-process>
 <process id="processd06909c28" taskpriority="0" logused="0" waitresource="KEY: 5:72057597585719296 (d2d87c26d920)" waittime="78785" ownerId="4436575948" transactionname="user_transaction" lasttranstarted="2017-04-13T07:39:17.590" XDES="0x3219d034e0" lockMode="U" schedulerid="3" kpid="133792" status="suspended" spid="106" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-04-13T07:39:17.657" lastbatchcompleted="2017-04-13T07:39:17.657" lastattention="1900-01-01T00:00:00.657" clientapp="Microsoft Dynamics AX" hostname="****" hostpid="11800" loginname="****" isolationlevel="read committed (2)" xactid="4436575948" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
  <executionStack>
   <frame line="1" stmtstart="72" stmtend="256" sqlhandle="0x0200000076a6a92ab1256af09321b056ab243f187342f9960000000000000000000000000000000000000000"/>
   <frame line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"/>
  </executionStack>
  <inputbuf>
(@P1 int,@P2 int,@P3 bigint,@P4 int)UPDATE PRODROUTEJOB SET JOBSTATUS=@P1,RECVERSION=@P2 WHERE ((RECID=@P3) AND (RECVERSION=@P4))   </inputbuf>
 </process>
</blocked-process>
<blocking-process>
 <process/>
</blocking-process>
</blocked-process-report>

這可以在當時相同資源的日誌記錄表中找到:Gist because of character limit

進一步的調查表明,在報告之前和之後,我有一個空阻塞程序的報告,我有相同的resourceid報告,這些resourceid確實有阻塞程序節點:

<blocked-process-report monitorLoop="1621636">
<blocked-process>
 <process id="processd06909c28" taskpriority="0" logused="0" waitresource="KEY: 5:72057597585719296 (d2d87c26d920)" waittime="73765" ownerId="4436575948" transactionname="user_transaction" lasttranstarted="2017-04-13T07:39:17.590" XDES="0x3219d034e0" lockMode="U" schedulerid="3" kpid="133792" status="suspended" spid="106" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-04-13T07:39:17.657" lastbatchcompleted="2017-04-13T07:39:17.657" lastattention="1900-01-01T00:00:00.657" clientapp="Microsoft Dynamics AX" hostname="***" hostpid="11800" loginname="***" isolationlevel="read committed (2)" xactid="4436575948" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
  <executionStack>
   <frame line="1" stmtstart="72" stmtend="256" sqlhandle="0x0200000076a6a92ab1256af09321b056ab243f187342f9960000000000000000000000000000000000000000"/>
   <frame line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"/>
  </executionStack>
  <inputbuf>
(@P1 int,@P2 int,@P3 bigint,@P4 int)UPDATE PRODROUTEJOB SET JOBSTATUS=@P1,RECVERSION=@P2 WHERE ((RECID=@P3) AND (RECVERSION=@P4))   </inputbuf>
 </process>
</blocked-process>
<blocking-process>
 <process status="sleeping" spid="105" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2017-04-13T07:40:31.417" lastbatchcompleted="2017-04-13T07:40:31.423" lastattention="1900-01-01T00:00:00.423" clientapp="Microsoft Dynamics AX" hostname="**" hostpid="11800" loginname="**" isolationlevel="read committed (2)" xactid="4436165115" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
  <executionStack/>
  <inputbuf>
(@P1 bigint,@P2 nvarchar(5),@P3 bigint,@P4 bigint,@P5 nvarchar(11),@P6 int,@P7 nvarchar(21),@P8 datetime2)SELECT T1.REGDATETIME,T1.REGDATETIMETZID,T1.WORKERPILOT,T1.WORKER,T1.WRKCTRIDPILOT,T1.REGTYPE,T1.PROFILEDATE,T1.JOBID,T1.JOBIDABS,T1.MATCHRECIDSTARTSTOP,T1.JOBACTIVE,T1.RESNO,T1.STARTITEMS,T1.GOODITEMS,T1.SCRAPITEMS,T1.FINISHEDCODE,T1.TMPGOODITEMS,T1.TMPSCRAPITEMS,T1.SYSMRPUPDATEREQUEST,T1.ERROR,T1.ERRORTXT,T1.TMPSTARTITEMS,T1.AUTOSTAMP,T1.ERRORSPECIFICATION,T1.COSTCATEGORY,T1.ONCALLACTIVITY,T1.TERMINALID,T1.PDSCWGOODITEMS,T1.PDSCWSCRAPITEMS,T1.PDSCWSTARTITEMS,T1.RETAILTERMINALID,T1.MODIFIEDDATETIME,T1.RECVERSION,T1.PARTITION,T1.RECID FROM JMGTERMREG T1 WHERE (((PARTITION=@P1) AND (DATAAREAID=@P2)) AND (((((WORKER=@P3) OR ((WORKER=@P4) AND (WRKCTRIDPILOT=@P5))) AND (REGTYPE=@P6)) AND (JOBID=@P7)) AND (REGDATETIME>=@P8))) ORDER BY T1.REGDATETIME   </inputbuf>
 </process>
</blocking-process>
</blocked-process-report>

使用srutzky提供的新腳本收集了新數據。 由於最大文章長度,它發佈在github上。

由於最初發布的數據沒有兩個會話ID,一些新數據已再次發佈在github上

新數據,包括github 上的連接

我目前無法測試這個理論,但是根據發佈到 GitHub 的最新擷取數據,我會說<process>節點為空的原因是它需要目前正在執行的請求(許多屬性都在sys.dm_exec_requests而不是 in sys.dm_exec_sessions) 並且沒有目前正在執行的請求,它無法報告任何詳細資訊,類似於如何執行INNER JOINbetween sys.dm_exec_requests並將sys.dm_exec_sessions排除 Session 處於活動狀態但由於沒有目前請求而空閒的行。

查看頂部數據集(monitorLoop值:1748823、1748824、1748825 和 1748827),我們可以看到以下內容:

  • the idof theblocked-process在每種情況下都是相同的:process2552c1fc28,唯一不同的屬性是waittime(可以理解的)。
  • 節點的屬性blocking-process顯示兩者lastbatchstarted的差異lastbatchcompleted
  • 節點的屬性blocking-process顯示相同的值spidxactid

那麼,阻塞程序的 SessionID 和 TransactionID 怎麼能在 4 個不同的查詢批次中相同呢?很簡單,一個顯式事務被啟動,然後這些批次被執行。並且因為這些是單獨的批次,所以在送出它們之間有時間,此時沒有目前請求,因此沒有要顯示的程序資訊(但會話和事務仍然存在)。

為了對此進行進一步研究,您可以從以下 T-SQL 中擷取有用資訊,sys.dm_exec_requests並將sys.dm_tran_locks其放置在 SQL Server 代理“事務 SQL 腳本 (T-SQL)”作業步驟中,將“數據庫”設置為您正在研究的一個(在本例中是 ID 為 6 的那個),並安排此作業每 10 秒執行一次。如果它們不存在,下面的 T-SQL 將在同一個數據庫中創建兩個表,然後如果任何請求本身阻塞,或者它是被阻塞的刪除或更新操作,則將填充“請求”表. 如果找到任何請求,它將嘗試擷取:

  • 阻塞程序的會話和請求資訊(這部分不假設有一個活動的請求,因此RIGHT JOIN至少要獲取會話資訊)
  • 被阻止和(希望)阻止程序的連接資訊。
  • ~~那些相同 session_id 的目前鎖(請記住,鎖資訊不能保證100% 準確,因為該資訊可能會在這兩個語句執行之間的時間內發生變化;不過,該資訊通常足夠好,足以值得捕捉)。~~此部分目前已被註釋掉。

SQL Server 代理 T-SQL 作業步驟:

-- !! Remember to set the "Database" for the T-SQL Job Step to
--    the DB that has database_id = 6 !!
SET NOCOUNT ON;
IF (OBJECT_ID(N'dbo.tmpBlockingResearch_Requests') IS NULL)
BEGIN
 -- Create requests capture table
 SELECT SYSDATETIME() AS [CaptureTime], req.*,
        ses.login_time, ses.[host_name], ses.[program_name], ses.host_process_id,
        ses.client_version, ses.client_interface_name, ses.security_id,
        ses.login_name, ses.nt_domain, ses.nt_user_name, ses.memory_usage,
        ses.total_scheduled_time, ses.endpoint_id, ses.last_request_start_time,
        ses.last_request_end_time, ses.is_user_process, ses.original_security_id,
        ses.original_login_name, ses.last_successful_logon, ses.last_unsuccessful_logon,
        ses.unsuccessful_logons, ses.authenticating_database_id
 INTO   dbo.tmpBlockingResearch_Requests
 FROM   sys.dm_exec_requests req
 INNER JOIN sys.dm_exec_sessions ses
         ON ses.[session_id] = req.[session_id]
 WHERE  1 = 0;
END;

IF (OBJECT_ID(N'dbo.tmpBlockingResearch_Connections') IS NULL)
BEGIN
 -- Create connections capture table
 SELECT SYSDATETIME() AS [CaptureTime], con.*
 INTO   dbo.tmpBlockingResearch_Connections
 FROM   sys.dm_exec_connections con
 WHERE  1 = 0;
END;

IF (OBJECT_ID(N'dbo.tmpBlockingResearch_Locks') IS NULL)
BEGIN
 -- Create locks capture table
 SELECT SYSDATETIME() AS [CaptureTime], loc.*
 INTO   dbo.tmpBlockingResearch_Locks
 FROM   sys.dm_tran_locks loc
 WHERE  1 = 0;
END;
---------------------------------
DECLARE @SessionIDs TABLE (SessionID SMALLINT NOT NULL,
                          BlockingSessionID SMALLINT NOT NULL);

INSERT INTO dbo.tmpBlockingResearch_Requests
OUTPUT inserted.[session_id], inserted.[blocking_session_id]
INTO   @SessionIDs ([SessionID], [BlockingSessionID])
 SELECT SYSDATETIME() AS [CaptureTime], req.*,
        ses.login_time, ses.[host_name], ses.[program_name], ses.host_process_id,
        ses.client_version, ses.client_interface_name, ses.security_id,
        ses.login_name, ses.nt_domain, ses.nt_user_name, ses.memory_usage,
        ses.total_scheduled_time, ses.endpoint_id, ses.last_request_start_time,
        ses.last_request_end_time, ses.is_user_process, ses.original_security_id,
        ses.original_login_name, ses.last_successful_logon, ses.last_unsuccessful_logon,
        ses.unsuccessful_logons, ses.authenticating_database_id
 FROM   sys.dm_exec_requests req
 INNER JOIN sys.dm_exec_sessions ses
         ON ses.[session_id] = req.[session_id]
 WHERE ses.[is_user_process] = 1
 AND   req.[database_id] = DB_ID()
 AND   (
         req.blocking_session_id IN (req.[session_id], -2, -3, -4)
   OR   (req.[command] IN (N'DELETE', N'UPDATE') AND req.[blocking_session_id] > 0)
       );

-- Get at least session info, if not also request info, on blocking process
INSERT INTO dbo.tmpBlockingResearch_Requests
 SELECT SYSDATETIME() AS [CaptureTime], req.*,
        ses.login_time, ses.[host_name], ses.[program_name], ses.host_process_id,
        ses.client_version, ses.client_interface_name, ses.security_id,
        ses.login_name, ses.nt_domain, ses.nt_user_name, ses.memory_usage,
        ses.total_scheduled_time, ses.endpoint_id, ses.last_request_start_time,
        ses.last_request_end_time, ses.is_user_process, ses.original_security_id,
        ses.original_login_name, ses.last_successful_logon, ses.last_unsuccessful_logon,
        ses.unsuccessful_logons, ses.authenticating_database_id
 FROM   sys.dm_exec_requests req
 RIGHT JOIN sys.dm_exec_sessions ses
         ON ses.[session_id] = req.[session_id]
 WHERE ses.[session_id] IN (SELECT DISTINCT [BlockingSessionID] FROM @SessionIDs);

-- If any rows are captured this time, try to capture their connection info
INSERT INTO dbo.tmpBlockingResearch_Connections
 SELECT SYSDATETIME() AS [CaptureTime], con.*
 FROM   sys.dm_exec_connections con
 WHERE  con.[session_id] IN (
                             SELECT [SessionID]
                             FROM @SessionIDs
                             UNION -- No "ALL" so it does DISTINCT
                             SELECT [BlockingSessionID]
                             FROM @SessionIDs
                            );

/*
-- If any rows are captured this time, try to capture their lock info
INSERT INTO dbo.tmpBlockingResearch_Locks
 SELECT SYSDATETIME() AS [CaptureTime], loc.*
 FROM   sys.dm_tran_locks loc
 WHERE  loc.[request_session_id] IN (
                                     SELECT [SessionID]
                                     FROM @SessionIDs
                                     UNION -- No "ALL" so it does DISTINCT
                                     SELECT [BlockingSessionID]
                                     FROM @SessionIDs
                                    );
*/

我認為您應該能夠通過打開一個查詢選項卡並執行以下操作來重現這一點:

CREATE TABLE dbo.tmp (Col1 INT);
BEGIN TRAN;
INSERT INTO dbo.tmp (Col1) VALUES (1);

然後,打開第二個查詢選項卡並執行以下命令:

UPDATE dbo.tmp
SET    Col1 = 2
WHERE  Col1 = 1;

PS 順便說一句,唯一沒有意義的是請求和會話資訊dbo.tmpBlockingResearch_Requests——仍然從不包含阻塞會話的行。但是我知道表變數中包含阻塞會話 ID,因為它確實為兩個 SessionID 拉入了鎖。這可能指向這樣一種情況,即在來自客戶端的“連接”關閉後,允許事務保持打開狀態,但由於連接池,連接仍保持不變。

由於鎖定升級,可能會發生阻塞事務。

這在 Microsoft 支持文章中進行了解釋:

如何解決由 SQL Server 中的鎖升級引起的阻塞問題

鎖升級不會導致大多數阻塞問題。要確定在遇到阻塞問題時是否發生鎖升級,請啟動包含 Lock:Escalation 事件的 SQL Profiler 跟踪。如果您沒有看到任何 Lock:Escalation 事件,則說明您的伺服器上沒有發生鎖升級,並且本文中的資訊不適用於您的情況。

如果發生鎖升級,請驗證升級的表鎖是否阻塞了其他使用者

……

檢查擴展事件(物理文件)以了解在阻塞程序事件之前發生的鎖升級事件。

解釋

有一篇微軟部落格文章更詳細地介紹了:

SQL Server 鎖升級和阻塞

第 2 步:收集鎖升級和阻塞程序報告事件。

SQL Server 不會自動擷取鎖升級和阻塞程序報告事件。為了知道這些事件是否正在發生,我們需要告訴 SQL Server 記錄它們。我們的團隊使用 Performance Analyzer for Microsoft Dynamics 工具來收集這些資訊。查看 Rod Hansen 的這篇文章,了解有關該工具以及如何使用它收集阻止詳細資訊的更多資訊。如果您只想使用 SQL Server Profiler,您需要收集的事件如下所示: …

擷取鎖升級和阻塞程序後,您必須確定鎖升級是否是阻塞程序的根本原因:

第 3 步:查看 SQL Server Profiler 中的跟踪。

有兩個主要指標可以告訴您阻塞是否與鎖升級有關。

首先,您會在阻塞程序報告事件之前看到一系列鎖升級事件。下面是從 Microsoft Dynamics 工具的性能分析器生成的跟踪中獲取的範例。這是在跟踪中尋找的一件事,但這並不意味著鎖升級會導致阻塞。…

並進一步

要驗證阻塞實際上與鎖升級有關,您需要查看阻塞程序報告的詳細資訊。在 TextData 部分中查找 waitresource(請參見下面的螢幕截圖)。如果 waitresource 以 OBJECT 開頭,我們知道被阻塞的語句正在等待表級鎖被釋放,然後才能繼續執行。如果 waitresource 以KEYPAG而不是 OBJECT 開頭,則該特定塊不涉及鎖升級。鎖升級總是會增加對 OJBECT 的鎖的範圍,無論它從哪裡開始

解決方案

(僅當上述匹配時)

解決方案顯然是打開跟踪標誌 1224,這將關閉鎖升級:

SQL Server 鎖升級和阻塞

如果您將這兩件事放在一起,那麼很可能是鎖升級導致了阻塞,並且您可能會從實施 SQL Server 跟踪標誌 1224 中受益。

Dynamics AX 的 SQL Server 跟踪標誌

跟踪標誌 1224 根據鎖的數量禁用鎖升級。啟用此跟踪標誌可以減少由於鎖升級而阻塞的可能性——我在許多 AX 實現中看到了這一點。這成為問題的最常見情況是當總體規劃需要在白天執行時

回答

最後,鎖升級可能是阻塞程序的根本原因。


替代解決方案(流程節點為空)

在對一些blocked_process_reports 進行進一步調查後,可以做出以下替代解釋。

擴展事件正在擷取當時與任何其他程序無關的blocked_process_reports。

爾格:它們必須因不同的原因而被阻止

我建議您從 SQL Server 上的 sys.dm_os_wait_stats 視圖中擷取等待類型的時間範圍,並將這些數字與測量期間發生的 blocks_process_reports 相關聯。Paul Randall 有一個很好的腳本: 將您的等待統計資訊發送給我,並獲得我的建議和 30 天的免費 Pluralsight 作為回報

腳本擷取目前計數器,等待 23 小時(可以修改),再次重新擷取目前計數器並比較它們,為您提供前 95% 的等待類型。你可以試一試,比如 1 小時,然後將 XEL 文件放在手邊。

您可能會發現一個等待類型(例如 LCK_M_SH,…),它告訴您您的儲存寫入速度很慢。或者您有一些其他成本(例如 CX_PACKET_WAITS,…)。某些事情正在減慢您的更新速度。然後,您可以查看 sys.dm_os_wait_stats 是否與帶有空節點的 blocked_process_reports 相關。

在某些情況下,被阻止的 SPID 被同一個 SPID 阻止:

安裝 SQL Server 2000 SP4 後,為閂鎖等待填充 sysprocesses 表中的阻塞列

當 SPID 正在等待 I/O 頁鎖存器時,您可能會注意到阻塞列簡要報告 SPID 正在阻塞自身。這種行為是鎖存器用於數據頁上的 I/O 操作方式的副作用。當執行緒發出 I/O 請求時,發出 I/O 請求的 SPID 將獲取頁面上的鎖存器。所有 SQL Server 2000 I/O 操作都是非同步的。因此,如果發出 I/O 請求的 SPID 必須等待請求完成,則 SPID 將嘗試在同一頁上獲取另一個鎖存器。該第二鎖存器被第一鎖存器阻塞。因此,被阻止的列報告 SPID 正在阻止自己。當 I/O 請求完成時,第一個鎖存器被釋放。然後,准許第二個鎖存請求。

替代答案

這進一步表明您可能遇到了 IO 問題。這些問題會導致“程序阻塞”,但沒有相關的外部 SPID。擴展事件可能不會在單獨的節點中報告程序/SPID。

引用自:https://dba.stackexchange.com/questions/168646