Lock Escalation e Count Discrepancy in lock_acquired Extended Event

Aug 31 2020

Sto cercando di capire perché in alcuni casi c'è una discrepanza nel conteggio dei blocchi sys.dm_tran_lockse sqlserver.lock_acquirednell'evento esteso. Ecco il mio script di repro, sto usando il StackOverflow2013database su SQL Server 2019 RTM, livello di compatibilità 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);

Successivamente aggiorno le statistiche della tabella con un conteggio di righe false di grandi dimensioni

/* Chaotic Evil. */
UPDATE STATISTICS dbo.HighQuestionScores WITH ROWCOUNT = 99999999999999;
DBCC FREEPROCCACHE WITH NO_INFOMSGS;

Quindi apro una transazione e aggiorno Scoreper Reputazione, ad esempio56

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;

Ottengo un sacco di blocchi di pagina (nonostante abbia un indice sulla reputazione). Immagino che le stime sbagliate abbiano davvero fatto un numero sull'ottimizzatore lì.

Ho anche ricontrollato usando sp_whoisactivee anch'esso restituisce le stesse informazioni.

<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>

Nel frattempo ho anche un evento esteso in esecuzione sqlserver.lock_acquiredseparatamente. Quando guardo i dati raggruppati vedo 8066 blocchi di pagina invece di 6159 iniziali

Sicuramente non vedo un'escalation di blocchi (verificata utilizzando l' sqlserver.lock_escalationevento), quindi immagino che la mia domanda sia perché l'evento esteso mostra una discrepanza con un numero maggiore di conteggi di blocchi?

Risposte

6 JoshDarnell Aug 31 2020 at 11:44

XE segnala l'acquisizione di un blocco di pagina ogni volta che viene aggiornata una riga (un evento per ciascuna delle 8066 righe interessate dall'aggiornamento). Tuttavia, queste righe vengono memorizzate solo su 6159 pagine univoche, il che spiega la discrepanza.

Non ho StackOverflow2013 su questa macchina, ma ho un'esperienza simile con SO2010:

  • 1368 righe aggiornate (e altrettanti XEvents attivati)
  • 958 blocchi di pagine

Puoi vedere le stesse pagine bloccate ripetutamente nell'output XE se ordini per resource_0:

Utilizzando DBCC PAGE:

DBCC TRACEON (3604); -- needed for the next one to work
GO

DBCC PAGE (StackOverflow2010, 1, 180020, 3);
GO

Vedo che la pagina 180020 contiene 163 record ( m_slotCnt = 163):

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                

E quei 3 di questi corrispondono ai criteri di aggiornamento (ho incollato l'output in Notepad ++ e ho cercato "Reputation = 56"):

Ecco la prima corrispondenza, come esempio:

Record Type = PRIMARY_RECORD        Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS
Record Size = 39                    
Memory Dump @0x000000EDB2B7821D

0000000000000000:   30001800 86540000 38000000 00000000 6041c2e4  0...T..8.......`AÂä
0000000000000014:   c3020000 04000801 00270045 00720069 006300    Ã........'.E.r.i.c.

Slot 9 Column 1 Offset 0x4 Length 4 Length (physical) 4

Id = 21638                          

Slot 9 Column 2 Offset 0x1f Length 8 Length (physical) 8

DisplayName = Eric                  

Slot 9 Column 3 Offset 0x8 Length 8 Length (physical) 8

Reputation = 56                     

Slot 9 Column 4 Offset 0x0 Length 0 Length (physical) 0

Score = [NULL]                      

Slot 9 Offset 0x0 Length 0 Length (physical) 0

KeyHashValue = (e1caffa60313)       
Slot 10 Offset 0x244 Length 43

Credo che questo comportamento sia dovuto alla natura pipeline dei piani di esecuzione e al modo in cui viene implementato questo XE specifico.