如何根据存储在每个页面标题中的 m_objId 和 m_indexId 字段计算分配单元 ID。
当 DBCC PAGE 转储页头的内容时,它会进行必要的计算和元数据查找,以便能够告诉您分配单元 ID、分区 ID、关系对象 ID 和关系索引 ID。基本上,下面的 DBCC PAGE 输出中以“元数据:”为前缀的所有内容都不会存储在页面本身上:
Page @0x00000004ED8A2000 m_pageId = (1:445) m_headerVersion = 1 m_type = 1 m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0xa000 m_objId (AllocUnitId.idObj) = 97 m_indexId (AllocUnitId.idInd) = 256 Metadata: AllocUnitId = 72057594044284928 Metadata: PartitionId = 72057594039304192 Metadata: IndexId = 0 Metadata: ObjectId = 599673184 m_prevPage = (0:0) m_nextPage = (0:0) pminlen = 8 m_slotCnt = 1 m_freeCnt = 8069 m_freeData = 121 m_reservedCnt = 0 m_lsn = (225:443:22) m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0 m_tornBits = 0 DB Frag ID = 1
公式如下:
- 取m_indexId并左移 48,得出值 A
- 取m_objId并左移 16,得出值 B
- AllocUnitId = A | B(其中 | 是逻辑 OR 运算)
使用上面的页面:
A = 256 << 48 = 72057594037927936
B = 97 << 16 = 6356992
AllocUnitId = 72057594044284928
使用 SQL Server 使用POWER函数执行此操作,因为 X 位的左移与乘以 2 的幂次 X 相同:
SELECT 256 * CONVERT (BIGINT, POWER (2.0, 48)) | 97 * CONVERT (BIGINT, POWER (2.0, 16));
然后您可以使用sys.system_internals_allocation_units和sys.partitions执行查找,如下所示:
SELECT [a].[container_id] AS [Partition ID], [p].[object_id] AS [Object ID], [p].[index_id] AS [Index ID] FROM sys.system_internals_allocation_units [a] JOIN sys.partitions [p] ON [p].[partition_id] = [a].[container_id] WHERE [a].[allocation_unit_id] = 72057594044284928; GO Partition ID Object ID Index ID -------------------- ----------- ----------- 72057594039304192 599673184 0
可以看到这些值与DBCC PAGE输出相匹配。
要将分配单元 ID 转换为在DBCC PAGE输出中看到的内容:
m_indexId = AllocUnitId >> 48
m_objId = ( AllocUnitId – ( m_indexId << 48)) >> 16
用于此的 T-SQL 涉及浮点数学,因为我们需要使用POWER的倒数:
DECLARE @alloc BIGINT = 72057594044284928; DECLARE @index BIGINT; SELECT @index = CONVERT (BIGINT, CONVERT (FLOAT, @alloc) * (1 / POWER (2.0, 48)) -- right shift, reciprocal of left shift ); SELECT CONVERT (BIGINT, CONVERT (FLOAT, @alloc - (@index * CONVERT (BIGINT, POWER (2.0, 48)))) * (1 / POWER (2.0, 16)) -- right shift, reciprocal of left shift ) AS [m_objId], @index AS [m_indexId]; GO m_objId m_indexId -------------------- -------------------- 97 256
这可以作为对DBCC CHECKDB无法处理的损坏数据库进行编程分析期间,以允许你作为最后的手段提取数据。