waitresource = "PAGE: 6: 3: 70133"
waitresource = "CLAVE: 6: 72057594041991168 (ce52f92a058c)"
A veces, habrá más información en ese XML gigante que está aprendiendo (los gráficos estancados contienen una lista de recursos que le ayudan a encontrar los nombres de los objetos y los índices), pero no siempre.
Este texto te ayudará a descifrarlos.
Toda la información que está aquí está en Internet en varios lugares, ¡simplemente está altamente distribuida! Quiero unir todo, desde DBCC PAGE hasta hobt_id y funciones %% physloc %% y %% lockres %% sin documentar.
Primero, hablemos de esperar en los bloqueos de PAGE y luego pasemos a los bloqueos de LLAVE.
1) waitresource = "PAGE: 6: 3: 70133" = Database_Id: FileId: PageNumber
Si su consulta está esperando en un bloqueo de PÁGINA, SQL Server le dará la URL de esa página.
Desglosando "PÁGINA: 6: 3: 70133" obtenemos:
- database_id = 6
- data_file_id = 3
- page_numer = 70133
1.1) Descifrar database_id
Busquemos el nombre de la base de datos usando la consulta:
SELECT
name
FROM sys.databases
WHERE database_id=6;
GO
Esta es la base de datos pública WideWorldImporters en mi SQL Server.
1.2) Buscando el nombre del archivo de datos, si está interesado
Vamos a usar data_file_id en el siguiente paso para encontrar el nombre de la tabla. Puede ir al siguiente paso, pero si está interesado en el nombre del archivo, puede encontrarlo ejecutando una consulta en el contexto de la base de datos encontrada, sustituyendo data_file_id en esta consulta:
USE WideWorldImporters;
GO
SELECT
name,
physical_name
FROM sys.database_files
WHERE file_id = 3;
GO
En la base de datos WideWorldImporters, este es un archivo llamado WWI_UserData y lo restauré en C: \ MSSQL \ DATA \ WideWorldImporters_UserData.ndf. (¡Vaya, me sorprendiste poniendo archivos en el disco del sistema! ¡No! Fue incómodo).
1.3) Obtenga el nombre del objeto de DBCC PAGE
Ahora sabemos que la página # 70133 en el archivo de datos 3 pertenece a la base de datos WorldWideImporters. Podemos ver el contenido de esta página usando la DBCC PAGE sin documentar y la marca de seguimiento 3604.
Nota: Prefiero usar DBCC PAGE en una copia de seguridad restaurada desde una copia de seguridad en algún lugar de otro servidor, porque es una cosa indocumentada. En algunos casos, puede conducir a la creación de un volcado ( comentario del traductor: el enlace, desafortunadamente, no conduce a ninguna parte, pero a juzgar por la URL, estamos hablando de índices filtrados ).
/* This trace flag makes DBCC PAGE output go to our Messages tab
instead of the SQL Server Error Log file */
DBCC TRACEON (3604);
GO
/* DBCC PAGE (DatabaseName, FileNumber, PageNumber, DumpStyle)*/
DBCC PAGE ('WideWorldImporters',3,70133,2);
GO
Desplazándose hacia abajo hasta los resultados, puede encontrar el object_id y el index_id.
¡Casi listo! Ahora puede encontrar los nombres de la tabla y el índice usando la consulta:
USE WideWorldImporters;
GO
SELECT
sc.name as schema_name,
so.name as object_name,
si.name as index_name
FROM sys.objects as so
JOIN sys.indexes as si on
so.object_id=si.object_id
JOIN sys.schemas AS sc on
so.schema_id=sc.schema_id
WHERE
so.object_id = 94623380
and si.index_id = 1;
GO
Y ahora vemos que la espera en el bloqueo estaba en el índice PK_Sales_OrderLines de la tabla Sales.OrderLines.
Nota: En SQL Server 2014 y versiones posteriores, el nombre del objeto también se puede encontrar utilizando el DMO no documentado sys.dm_db_database_page_allocations. Pero tienes que consultar todas las páginas de la base de datos, lo que no se ve muy bien para bases de datos grandes, así que usé DBCC PAGE.
1.4) ¿Puedes ver los datos en la página que fue bloqueada?
Nuuu, sí. Pero ... ¿estás seguro de que realmente lo necesitas?
Es lento incluso en mesas pequeñas. Pero esto es genial, así que ya que has leído hasta aquí ... ¡hablemos de %% physloc %%!
%% physloc %% es una pieza de magia indocumentada que devuelve una identificación física para cada entrada. Puede usar %% physloc %% junto con sys.fn_PhysLocFormatter en SQL Server 2008 y versiones posteriores .
Ahora que sabemos que queríamos bloquear la página en Sales.OrderLines, podemos ver todos los datos en esta tabla, que está almacenada en el archivo de datos # 3 en la página # 70133, usando la siguiente consulta:
Use WideWorldImporters;
GO
SELECT
sys.fn_PhysLocFormatter (%%physloc%%),
*
FROM Sales.OrderLines (NOLOCK)
WHERE sys.fn_PhysLocFormatter (%%physloc%%) like '(3:70133%'
GO
Como dije, es lento incluso en mesas pequeñas. Agregué NOLOCK a la consulta porque todavía no tenemos garantías de que los datos que queremos ver sean exactamente los mismos que cuando se encontró el bloqueo, por lo que podemos hacer lecturas sucias de manera segura.
Pero, hurra, la consulta me devuelve las mismas 25 filas por las que luchó nuestra consulta.
Basta de bloqueos de PÁGINA. ¿Qué pasa si estamos esperando un bloqueo de LLAVE?
2) waitresource = "KEY: 6: 72057594041991168 (ce52f92a058c)" = Database_Id, HOBT_Id (hash mágico que se puede descifrar con %% lockres %% si realmente lo desea)
Si su consulta intenta bloquear una entrada de índice y se bloquea a sí misma, obtendrá un tipo de dirección completamente diferente.
Al desglosar "6: 72057594041991168 (ce52f92a058c)" en partes, obtenemos:
- database_id = 6
- hobt_id = 72057594041991168
- hash mágico = (ce52f92a058c)
2.1) Descifrar database_id
¡Esto funciona exactamente de la misma manera que con el ejemplo anterior! Busque el nombre de la base de datos mediante la consulta:
SELECT
name
FROM sys.databases
WHERE database_id=6;
GO
En mi caso, esta es la misma base de datos de WideWorldImporters .
2.2) Descifrar el hobt_id
En el contexto de la base de datos encontrada, debe ejecutar una consulta en sys.partitions con un par de combinaciones que lo ayudarán a determinar los nombres de la tabla y el índice ...
USE WideWorldImporters;
GO
SELECT
sc.name as schema_name,
so.name as object_name,
si.name as index_name
FROM sys.partitions AS p
JOIN sys.objects as so on
p.object_id=so.object_id
JOIN sys.indexes as si on
p.index_id=si.index_id and
p.object_id=si.object_id
JOIN sys.schemas AS sc on
so.schema_id=sc.schema_id
WHERE hobt_id = 72057594041991168;
GO
Me dice que la solicitud estaba esperando en el bloqueo de Application.Countries usando el índice PK_Application_Countries.
2.3) Ahora algo de magia %% lockres %% - si quieres saber qué registro estaba bloqueado
Si realmente quiero saber en qué fila se necesitaba el bloqueo, puedo averiguarlo consultando la tabla. Podemos usar la función %% lockres %% sin documentar para encontrar la entrada que coincida con el hash mágico.
Tenga en cuenta que esta consulta escaneará toda la tabla y, en tablas grandes, esto puede no ser divertido en absoluto:
SELECT
*
FROM Application.Countries (NOLOCK)
WHERE %%lockres%% = '(ce52f92a058c)';
GO
Agregué NOLOCK ( siguiendo el consejo de Klaus Aschenbrenner en Twitter ) porque el bloqueo puede ser un problema. Solo queremos ver lo que hay ahora, y no lo que estaba cuando comenzó la transacción. No creo que la coherencia de los datos sea importante para nosotros.
¡Voila, el récord por el que luchamos!
Agradecimientos y lecturas adicionales
No recuerdo quién fue el primero en describir muchas de estas cosas, pero aquí hay dos publicaciones sobre las cosas menos documentadas que te pueden gustar:
- La publicación de Paul Randal en %% physloc %% y sys.fn_PhysLocFormatter (como hacemos con nuestros datos en el primer ejemplo)
- Una pregunta en StackOverflow sobre el uso de %% lockres %% (como encontramos los datos en el segundo ejemplo). Una de las respuestas va a la publicación de Grant Fritchey sobre %% lockres %%, escrita en 2010 .