翻译如何通过页ID知道表名
翻译如何通过页ID知道表名
原文:Finding a table name from a page ID
原文地址:http://www.sqlskills.com/blogs/paul/finding-table-name-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 = 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
|
如果你得到上面的结果,那么可能有两种原因:
- 你使用了错误的数据库上线文
- 数据库的元数据(metadata)已经被损坏,所以你只好等待DBCC CHECKDB结束。
根据我的经验,很有可能使第一种情况。你可以在suspect_pages输出的database_id,然后将它带入到DB_NAME得到数据库名称,在正确的数据库上下文中,再试一次:
USE [company]; GO SELECT OBJECT_NAME (245575913); GO -------------------------------------------------------------------------------------------------------------------------------- CustomerNames |