Aru*_*ath 5 sql-server lock-escalation locking
我试图理解为什么在某些情况下锁定计数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);
Run Code Online (Sandbox Code Playgroud)
接下来我用一个大的假行数更新表统计信息
/* Chaotic Evil. */
UPDATE STATISTICS dbo.HighQuestionScores WITH ROWCOUNT = 99999999999999;
DBCC FREEPROCCACHE WITH NO_INFOMSGS;
Run Code Online (Sandbox Code Playgroud)
然后我打开一个交易并更新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;
Run Code Online (Sandbox Code Playgroud)
我得到了一堆页面锁(尽管有声誉索引)。我猜测错误的估计确实对那里的优化器产生了影响。
我还仔细检查了使用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>
Run Code Online (Sandbox Code Playgroud)
同时,我还有一个sqlserver.lock_acquired单独运行的扩展事件。当我查看分组数据时,我看到8066页锁定而不是初始6159
我绝对没有看到锁升级(使用sqlserver.lock_escalation事件验证),所以我想我的问题是为什么扩展事件显示出与更多锁计数的差异?
每次更新行时,XE 都会报告获取页面锁(受更新影响的 8066 行中的每一行都有一个事件)。但是,这些行仅存储在 6159 个唯一页面上,这就解释了这种差异。
我在这台机器上没有 StackOverflow2013,但是在 SO2010 上得到了类似的体验:
如果按resource_0以下方式排序,您可以在 XE 输出中看到相同的页面被重复锁定:
使用DBCC PAGE:
DBCC TRACEON (3604); -- needed for the next one to work
GO
DBCC PAGE (StackOverflow2010, 1, 180020, 3);
GO
Run Code Online (Sandbox Code Playgroud)
我可以看到第 180020 页上有 163 条记录 ( m_slotCnt = 163):
DBCC TRACEON (3604); -- needed for the next one to work
GO
DBCC PAGE (StackOverflow2010, 1, 180020, 3);
GO
Run Code Online (Sandbox Code Playgroud)
其中 3 个符合更新条件(我将输出粘贴到 notepad++ 并搜索“Reputation = 56”):
以第一场比赛为例:
Page @0x000002C278C62000
m_pageId = (1:180020) m_headerVersion = 1 m_type = 1
m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0x0
m_objId (AllocUnitId.idObj) = 174 m_indexId (AllocUnitId.idInd) = 256
Metadata: AllocUnitId = 72057594049331200
Metadata: PartitionId = 72057594044350464 Metadata: IndexId = 1
Metadata: ObjectId = 1525580473 m_prevPage = (1:180019) m_nextPage = (1:180021)
pminlen = 24 m_slotCnt = 163 m_freeCnt = 31
m_freeData = 7835 m_reservedCnt = 0 m_lsn = (203:19986:617)
m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0
m_tornBits = 1894114769 DB Frag ID = 1
Run Code Online (Sandbox Code Playgroud)
我相信这种行为是由于执行计划的流水线性质,以及这个特定 XE 的实现方式。