Buscar objetos dañados por número de página dañado en MS SQL Server 2005

Uno de estos días, una de las bases de datos de MS SQL Server cambió a Sospechoso, había un mensaje de error en el registro:
Mensaje 7105, Nivel 22, Estado 9, Línea 14
ID de base de datos 6, página (1: 386499), la ranura 0 para el nodo de tipo de datos LOB no existe. Esto generalmente es causado por transacciones que pueden leer datos no confirmados en una página de datos. Ejecute DBCC CHECKTABLE.

La base de datos se transfirió a Emergencia e intentó ejecutar DBCC CHECKDB, pero la ejecución se interrumpió de inmediato:
Mensaje 8921, Nivel 16, Estado 1, Línea 13
Cheque terminado. Se detectó una falla al recopilar datos. Posiblemente tempdb fuera del espacio o una tabla del sistema es inconsistente. Verificar errores anteriores.
Mensaje 7105, Nivel 22, Estado 9, Línea 13
ID de base de datos 6, página (1: 386499), la ranura 0 para el nodo de tipo de datos LOB no existe. Esto generalmente es causado por transacciones que pueden leer datos no confirmados en una página de datos. Ejecute DBCC CHECKTABLE.

El comando DBCC CHECKALLOC se interrumpió con un error similar. Todo fue complicado por el hecho de que SQL Server era la versión 9.0.1399, es decir RTM, sin ninguna actualización.

Los intentos de usar la sugerencia TABLOCK y aumentar explícitamente el nivel de aislamiento de la transacción no condujeron a nada (había suficiente espacio en disco con tempdb y DBCC CHECKALLOC con WITH ESTIMATEONLY falló con el mismo error). Realmente no quería rodar SP a un servidor con una base de datos dañada, y era completamente incomprensible con qué objeto específico era el problema. Además, parecía que el mensaje DBCC CHECKDB tenía poco que ver con la realidad, ya que había un registro en msdb.dbo.suspect_pages, pero el número de página era diferente del que imprimió DBCC CHECKDB.

Para seguir las instrucciones DBCC CHECKDB y ejecutar DBCC CHECKTABLE, necesita conocer la tabla. Y después de una larga búsqueda, se encontró una instrucción.
Nota
Pido disculpas porque los números de la tabla en los mensajes de error y en el código no coinciden. Tomé errores de los registros, y luego ejecuté el código en un entorno de prueba en otra base en vivo.

Utilizamos el siguiente algoritmo para determinar el object_id de ambas páginas, desde DBCC CHECKDB y sospecha_páginas. El problema estaba en la página de sospecha_páginas

Lo primero que debe hacer es ejecutar (en el contexto de una base de datos dañada) es DBCC PAGE (database_id, file_id, page_id, printopt):

DBCC TRACEON (3604); DBCC PAGE(5, 1, 3242342, 0) DBCC TRACEOFF (3604); 

ya sea:

 DBCC PAGE(5, 1, 3242342, 0) WITH TABLERESULTS. 

Si tienes suerte (o estás jugando en una base en vivo), como resultado verás el campo Metadata: ObjectId y, de hecho, el object_id deseado:



Sin embargo, si usted, como nosotros, tiene mala suerte, verá lo siguiente:
Metadatos: = No disponible en DB fuera de línea
Si los metadatos no están disponibles, no todo se pierde, en este caso, necesitamos el campo m_objId (AllocUnitId.idObj). Si m_objId = 255, el problema es, cierre el artículo y busque otra cosa (intente escribir todo lo que pueda y elimine los datos, ejecute DBCC CHECKDB con los parámetros de "recuperación" a ciegas, etc.).
La captura de pantalla muestra que tengo m_objId = 9931, es decir puede continuar.

Ahora necesita hacer algunos cálculos para calcular la ID de la Unidad de asignación (puede encontrar más información sobre las Unidades de asignación aquí ):
ID de unidad de asignación = m_objid * 65536 + (2 ^ 56)
En nuestro caso:
ID de unidad de asignación = 9931 * 65536 + (2 ^ 56) = 72057594688765952

Entonces, conociendo la ID de la Unidad de asignación, puede ver lo que tenemos en la vista del sistema sys.allocation_units :

 SELECT * FROM sys.allocation_units WHERE allocation_unit_id = 72057594688765952 



Y allí, en el tipo de caso = 1 o 3 (IN_ROW_DATA, ROW_OVERFLOW_DATA), la columna container_id = sys.partitions.hobt_id ("Heap-Or-B-Tree ID"), es decir Puedes ejecutar la solicitud:

 SELECT * FROM sys.partitions WHERE hobt_id = 72057594661437440 



Y aquí ya hay el object_id y index_id correctos. Ahora puede ver lo que tenemos en sys.objects y sys.indexes, y simplemente ejecutar:

 SELECT OBJECT_NAME(object_id) 

Afortunadamente, tanto en la situación real como aquí, el índice no agrupado resultó ser confirmado, después de la reestructuración de la cual todo volvió a la normalidad (de hecho, no, pero esa es otra historia).

Referencias
Cómo usar la PÁGINA DBCC
Solución de problemas y corrección de daños en el nivel de página del servidor SQL
¿Qué son las unidades de asignación?
Encontrar un nombre de tabla a partir de una ID de página
sys.allocation_units

Source: https://habr.com/ru/post/es430824/


All Articles