J'ai une paire de requêtes qui, sous isolement sérialisable, provoquent un verrouillage RX-X. Cependant, lorsque j'utilise des événements étendus pour regarder l'acquisition de verrouillage, l'acquisition de verrouillage RX-X n'apparaît jamais, elle est uniquement publiée. D'où est ce que ça vient?
Voici ma table:
CREATE TABLE dbo.LockTest (
ID int identity,
Junk char(4)
)
CREATE CLUSTERED INDEX CX_LockTest --not unique!
ON dbo.LockTest(ID)
--preload some rows
INSERT dbo.LockTest
VALUES ('data'),('data'),('data')
Voici mon lot de problèmes:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN
INSERT dbo.LockTest
VALUES ('bleh')
SELECT *
FROM dbo.LockTest
WHERE ID = SCOPE_IDENTITY()
--ROLLBACK
Je vérifie les verrous détenus par cette session et vois RX-X:
SELECT resource_type, request_mode, request_status, resource_description
FROM sys.dm_tran_locks
WHERE request_session_id = 72 --change SPID!
Mais j'ai également un événement étendu sur lock_acquired
et lock_released
. Je le filtre sur l'id_object_id associé approprié ... il n'y a pas de RX-X.
Après avoir exécuté la restauration, je vois RX-X (LAST_MODE) publié, même s'il n'a jamais été acquis.
J'ai regardé tous les verrous dans les événements étendus - pas de filtrage. Aucun verrou RX-X acquis.
J'ai aussi essayé Profiler: mêmes résultats (sauf bien sûr qu'il obtient le bon nom ... pas de "LAST_MODE").
J'ai exécuté le XE pour les escalades de verrous - il n'est pas là.
Il n'y a pas de XE spécifiquement pour les conversions, mais j'ai pu confirmer qu'au moins la conversion de verrouillage U en X est capturée par lock_acquired
Il convient également de noter le RI-N qui est acquis mais jamais publié. Mon hypothèse actuelle est que le RX-X est un verrou de conversion, comme décrit ici . Il y a des verrous de plage de clés qui se chevauchent dans mon lot qui semblent devoir être qualifiés pour la conversion, mais le verrou RX-X n'est pas dans la table de conversion.
D'où vient ce verrou et pourquoi n'est-il pas récupéré par les événements étendus?
L'insertion sur une seule ligne acquiert un verrou X
(exclusif) sur la nouvelle ligne.
SELECT
tente d'acquérir une plage partagée, une clé partagée (RangeS-S
) fermer à clé.
Cette demande est signalée par le lock_acquired
Événement étendu en mode = RS_S
.
Il est signalé par la classe d'événements Profiler Lock:Acquired
comme mode 13 (LCK_M_RS_S
).
Le mode demandé est combiné avec le mode de verrouillage exclusif existant dans Lock::CalculateGrantMode
dans sqlmin.dll
. Il n'y a pas de mode combiné de plage partagée, clé exclusive (RangeS-X
) donc le résultat du calcul est exclusif à la plage, exclusif à la clé (RangeX-X
), qui se trouve être le mode 15.
Le calcul du mode d'autorisation ci-dessus est effectué juste avant que l'événement étendu soit généré par lck_ProduceExtendedEvent<XeSqlPkg::lock_acquired>
. Néanmoins, le profileur et les événements étendus enregistrent le demandéRangeS-S
mode, pas le mode de verrouillage résultant RangeX-X
. Ceci est contraire à la limitation documentation , qui dit:
Mode | int | Mode résultant après l'acquisition du verrou.
La colonne mode de l'événement étendu ne contient aucune documentation et la description dans les métadonnées est vide. Peut-être que Microsoft eux-mêmes n'était même pas sûr du comportement.
J'ai souvent pensé qu'il serait plus utile que les événements de verrouillage signalent les modes demandé et résultant, mais ce n'est pas ce que nous avons. L'arrangement actuel rend pratiquement impossible le suivi et la correspondance de l'acquisition et de la libération des verrous.
Il y a peut-être une bonne raison de signaler les verrous de cette façon. S'il ne répond pas à vos besoins, vous pouvez ouvrir un dossier de support auprès de Microsoft ou créer un élément Azure Feedback.
LAST_MODE
Le mystérieux LAST_MODE
est quelque chose qu'Erik Darling a remarqué avant . C'est le plus haut map_key
valeur dans la liste des modes de verrouillage exposés par sys.dm_xe_map_values
:
SELECT
DXMV.map_key,
DXMV.map_value
FROM sys.dm_xe_map_values AS DXMV
WHERE
DXMV.[name] = N'lock_mode'
ORDER BY
DXMV.map_key;
╔═════════╦═══════════╗
║ map_key ║ map_value ║
╠═════════╬═══════════╣
║ 0 ║ NL ║
║ 1 ║ SCH_S ║
║ 2 ║ SCH_M ║
║ 3 ║ S ║
║ 4 ║ U ║
║ 5 ║ X ║
║ 6 ║ IS ║
║ 7 ║ IU ║
║ 8 ║ IX ║
║ 9 ║ SIU ║
║ 10 ║ SIX ║
║ 11 ║ UIX ║
║ 12 ║ BU ║
║ 13 ║ RS_S ║
║ 14 ║ RS_U ║
║ 15 ║ RI_NL ║
║ 16 ║ RI_S ║
║ 17 ║ RI_U ║
║ 18 ║ RI_X ║
║ 19 ║ RX_S ║
║ 20 ║ RX_U ║
║ 21 ║ LAST_MODE ║
╚═════════╩═══════════╝
La structure de la mémoire accessible via le DMV (en utilisant sqlmin!CMapValuesTable
) est stocké à partir de l'adresse sqlmin!XeSqlPkg::g_lock_mode
. Chaque entrée de 16 octets dans la structure contient le map_key
et un pointeur sur la chaîne renvoyée par map_value
par le streaming TVF.
Les chaînes sont stockées exactement comme indiqué dans le tableau ci-dessus (mais pas dans cet ordre). Il semble que ce soit une erreur que l'entrée 21 ait un map_value
de "LAST_MODE" au lieu du "RX_X" attendu. Erik Darling a a signalé le problème sur Azure Feedback .