首页 > 代码库 > 翻译如何通过页ID知道表名

翻译如何通过页ID知道表名

翻译如何通过页ID知道表名

 原文:Finding a table name from a page ID

 译者:如果在一些特殊情况下只知道页ID,如何知道这是哪个表的页呢?本文主要解决这个问题。

 

正文:

想象一下某个早晨,你回去工作发现在夜间有一些新行插入到msdb.dbo.suspect_pages表中。通常你做的第一件事是运行DBCC CHECKDB,但是如果你的数据库有几个TB,这样可能得运行几个小时才能知道问题出在哪里——哪个表丢失了数据。但是你想到尽快找到哪个表,这样你可以选择灾难恢复方案。

 另一个情形是:你在找出性能差的查询——运行我的脚本,用sys.dm_os_waiting_tasks来查看当前等待的线程,你看到许多PAGELATCH_EX等待,你需要通过sys.dm_os_waiting_tasks的resource_description列中的页ID知道哪个表。

 回到第一种情形,通过suspect_pages表得到数据比较简单:

SELECT * FROM [msdb].[dbo].[suspect_pages];

GO

database_id file_id     page_id              event_type  error_count last_update_date

----------- ----------- -------------------- ----------- ----------- -----------------------

6           1           295                  2           2          2014-09-25 01:18:22.910

 

 要发现表名,首先需要使用DBCC PAGE。DBCC PAGE语法如下:

dbcc page ( {‘dbname‘ | dbid}, filenum, pagenum [, printopt={0|1|2|3} ])

 你可以只使用PRINT OPTION 0,这样只是显示表头。你同时必须使用TRACE FLAG 3604来获得DBCC PAGE的输出——这相当安全。你们使用suspect_pages的输出,DBCC PAGE的返回如下:

DBCC TRACEON (3604);

DBCC PAGE (6, 1, 295, 0);

DBCC TRACEOFF (3604);

GO

PAGE: (1:295)

 

BUFFER:

 

BUF @0x00000004FD8C7980

 

bpage = 0x00000004A2D14000          bhash = 0x0000000000000000          bpageno = (1:295)

bdbid = 6                           breferences = 0                     bcputicks = 0

bsampleCount = 0                    bUse1 = 55116                       bstat = 0x809

blog = 0x15ab215a                   bnext = 0x0000000000000000         

 

PAGE HEADER:

 

Page @0x00000004A2D14000

 

m_pageId = (1:295)                  m_headerVersion = 17                m_type = 17

m_typeFlagBits = 0x0                m_level = 0                         m_flagBits = 0x8200

m_objId (AllocUnitId.idObj) = 84    m_indexId (AllocUnitId.idInd) = 256

Metadata: AllocUnitId = 72057594043432960

Metadata: PartitionId = 72057594039042048                                Metadata: IndexId = 0

Metadata: ObjectId = 245575913      m_prevPage = (0:0)                  m_nextPage = (0:0)

pminlen = 8008                      m_slotCnt = 1                       m_freeCnt = 83

m_freeData = http://www.mamicode.com/8107 m_reservedCnt = 0 m_lsn = (35:200:9)

m_xactReserved = 0                  m_xdesId = (0:0)                    m_ghostRecCnt = 0

m_tornBits = 1093512791             DB Frag ID = 1                     

 

Allocation Status

GAM (1:2) = ALLOCATED               SGAM (1:3) = ALLOCATED

PFS (1:1) = 0x64 MIXED_EXT ALLOCATED 100_PCT_FULL                        DIFF (1:6) = CHANGED

ML (1:7) = NOT MIN_LOGGED

 

 我们感兴趣的是从metadata开始的部分。这些字段并不存放在数据本身之中。当初为SQL SERVER 2005重写DBCC PAGE时,我加入了metadata输出部分——这样比较容易找到该页所属的对象ID和索引ID(就像在SQL SERVER 7.0和2000中m_objId和m_indexId一样)。

 Metadata:objectId就是我们想要的。如果你看到它是99,那么请打住——因为这意味着损坏页是分配系统的一部分——并不是一个表的页,你需要等待DBCC CHECKDB结束来知道哪个区(extent)坏了。

 如果你看到ObjectId是0,这意味着没有发现metadata数据。这可能是以下原因:

1)    自从损坏被记录后,表所包含的该页已经被删除。

2)    系统目录由于某种原因损坏。

3)    页损坏,所以导致不正确的值用来查找metadata(当然查不到了)。

不管上面的什么哪一种情况,你必须等待DBCC CHECKDB结束来知道哪个区(extent)发生了破坏。

 如果ObjectId不是0或者99,你可以将该值放到OBJECT_NAME函数中来获得表名:

 

SELECT OBJECT_NAME (245575913);

GO

----------------------------------------------------------------------------------------

NULL

 

 如果你得到上面的结果,那么可能有两种原因:

  1. 你使用了错误的数据库上线文
  2. 数据库的元数据(metadata)已经被损坏,所以你只好等待DBCC CHECKDB结束。

 根据我的经验,很有可能使第一种情况。你可以在suspect_pages输出的database_id,然后将它带入到DB_NAME得到数据库名称,在正确的数据库上下文中,再试一次:

USE [company];

GO

SELECT OBJECT_NAME (245575913);

GO

--------------------------------------------------------------------------------------------------------------------------------

CustomerNames

 

翻译如何通过页ID知道表名