我试图理解为什么在某些情况下锁定计数sys.dm_tran_locks
和扩展事件存在差异。sqlserver.lock_acquired
这是我的复制脚本,我StackOverflow2013
在 SQL Server 2019 RTM 上使用数据库,兼容级别 150。
/* Initial Setup */
IF OBJECT_ID('dbo.HighQuestionScores', 'U') IS NOT NULL
DROP TABLE dbo.HighQuestionScores;
CREATE TABLE dbo.HighQuestionScores
(
Id INT PRIMARY KEY CLUSTERED,
DisplayName NVARCHAR(40) NOT NULL,
Reputation BIGINT NOT NULL,
Score BIGINT
)
INSERT dbo.HighQuestionScores
(Id, DisplayName, Reputation, Score)
SELECT u.Id,
u.DisplayName,
u.Reputation,
NULL
FROM dbo.Users AS u;
CREATE INDEX ix_HighQuestionScores_Reputation ON dbo.HighQuestionScores (Reputation);
接下来,我使用大量假行数更新表统计信息
/* Chaotic Evil. */
UPDATE STATISTICS dbo.HighQuestionScores WITH ROWCOUNT = 99999999999999;
DBCC FREEPROCCACHE WITH NO_INFOMSGS;
然后我打开一个事务并更新Score
声誉,比如说56
BEGIN TRAN;
UPDATE dbo.HighQuestionScores
SET Score = 1
WHERE Reputation = 56 /* 8066 records */
AND 1 = (SELECT 1);
/* Source: https://www.erikdarlingdata.com/sql-server/helpers-views-and-functions-i-use-in-presentations/ Thanks, Erik */
SELECT *
FROM dbo.WhatsUpLocks(@@SPID) AS wul
WHERE wul.locked_object = N'HighQuestionScores'
ROLLBACK;
我得到了一堆页面锁(尽管有一个关于声誉的索引)。我猜糟糕的估计确实在那里的优化器上做了一个数字。
我还仔细检查了使用sp_whoisactive
,它也返回了相同的信息。
<Object name="HighQuestionScores" schema_name="dbo">
<Locks>
<Lock resource_type="OBJECT" request_mode="IX" request_status="GRANT" request_count="1" />
<Lock resource_type="PAGE" page_type="*" index_name="PK__HighQues__3214EC072EE1ADBA" request_mode="X" request_status="GRANT" request_count="6159" />
</Locks>
</Object>
同时,我还有一个sqlserver.lock_acquired
单独的扩展活动。当我查看分组数据时,我看到8066页锁而不是初始6159
我绝对看不到锁升级(使用sqlserver.lock_escalation
事件验证),所以我想我的问题是为什么扩展事件显示与更高数量的锁计数存在差异?
XE 报告每次更新行时获取页锁(受更新影响的 8066 行中的每一行都有一个事件)。但是,这些行仅存储在 6159 个唯一页面上,这解释了差异。
我在这台机器上没有 StackOverflow2013,但使用 SO2010 获得了类似的体验:
如果您按以下方式排序,则可以在 XE 输出中看到相同的页面被重复锁定
resource_0
:使用
DBCC PAGE
:我可以看到页面 180020 上有 163 条记录(
m_slotCnt = 163
):其中 3 个符合更新条件(我将输出粘贴到 notepad++ 并搜索“Reputation = 56”):
这是第一场比赛,例如:
我相信这种行为是由于执行计划的流水线性质,以及这种特定 XE 的实现方式。