SQL Server 数据库内部存储基础
http://blog.163.com/shi_123@126/blog/static/169899162009338527558/
本节包含了SQL Server 数据库系统目录及表的内部数据存储。虽然在不理解内部细节的情况下也可以对SQL Server 数据库进行修复,但存储数据的细节将有助于开发有效的数据库修复程序,大大提高恢复成功率。(如果不需要深入了解这些信息,也可以跳过这部分讨论,进入关于恢复部分。)
SQL Server 数据库有着良好的数据备份和恢复机制,但仍然不能避免由于数据库损坏,造成数据丢失。造成SQL Server数据库损坏的重要原因有:
(1) 操作问题,包括冷启动系统、热插拔硬盘、删除一些数据库文件等;
(2) 硬件问题,包括磁盘控制器的问题等;
(3) 操作系统问题,包括与系统相关的一些致命错误等。
修复一个受损的SQL 数据库,可以利用 SQL Server 自身的修复机制。但当SQL数据库损坏严重时,就要想办法直接从 MDF 文件的表中恢复原始数据。因此我们必须对MDF 文件内部存储格式有着详细的了解。
6.1.1 数据页
SQL Server的MDF文件是页式存储格式。文件被划分成若干数据页。数据页是包含所有非文本或图像的数据的结构。就像使用SQL Server中的其他类型的页面一样,数据页面具有8KB(或8192字节)的固定大小。它们由三个主要部分组成:页面标题、数据行和行偏移量数组,如图6-1所示。
页面标题
96字节
数据行
行偏移量数组
图6-1 数据页的结构
页面标题
正如在图6-1中看到的,在每个数据页中,页面标题占用了前96个字节(剩下的8096字节用于数据和行偏移量)。表6-1显示的是页面标题所包含的信息。
字段 包含
pageID 该页面在数据库中的文件编号和页码
nextPage 如果该页面处于一个页面链中,那么该字段表示下一个页面的案件编号和页码
prevPage 如果该页面处于一个页面链中,那么该字段表示上一个页面的案件编号和页码
objID 该页面所属的对象的ID
lsn 用于更改和更新该页面的日志序列号(LSN)值
slotCnt 该页面中所用的总的槽(行)数
level 该页面在索引中的级别(对于叶页通常为0)
indexId 该页面的索引ID(对于数据页面通常为0)
freeData 该页面中的第一个自由空间的字节偏移量
pminlen 行的固定长度部分的字节数
freeCnt 页面中的自由字节数
reservedCnt 由所有事务预留的字节数
xactresenved 由最近启动的事务预留的字节数
tornBits 每个扇区1位,用于检测残缺页的写
flagBits 包含关于页面其他信息的2字节位图
表6-1 页面标题所包含的信息
数据页的页号是从0开始的,图6-2中的右下方区域是一个.MDF文件的第0页的标题信息的十六进制数显示方式。右上方区域是第0页标题信息的结构化显示方式。左侧区域显示的是页号和在文件中的偏移。
数据行
页面标题下面是存储表实际数据行和区域。一个数据行最大的大小是8060字节。一个数据行不能跨越多个页面(文本或图像列除外,它们可以被存储在它们自己独立的页面中)。给定页面中存储的行数是根据表的结构及所存储的数据变化的。全部是固定长度列的表通常对于每个页面将存储相同数目的行;可变长度行将根据所输入的数据的实际长度,来决定存储能够容纳的行数。保持较短的行长度将会在一个页面中包含更多的行,从而减少I/O并提高缓存命中率。
数据恢复
行偏移量数组
行偏移量数组是一块2字节的条目,每个条目指出页面中相应数据行开始的偏移量。每一行在该数组中有一个2字节的条目(正如“字符数据类型”部分中所讨论的,每个行需要10个字节的开销)。虽然这些字节没有被存储在包含数据的行中,但它们确实影响了一个页面所能包含的行数。
行偏移量数组指出一个页面中的行的逻辑顺序。例如,如果一个表具有群集索引,那么SQL Sever将以群集索引键的顺序来存储这些行。这并不意味着这些行将以群索引键的顺序物理地存储在页面中。相反,偏移量数组中的0槽指的是顺序中的第一行,1槽指的是第二行,以此类推。正如我们稍后在介绍实际的页面时将要看到的,这些行的偏移量可以位于页面中的任何位置。
对于表中的每一行,没有任何内部的全局行编号。SQL Sever组合使用页面中的文件编号、页码和槽编号来唯一地标识表中的每一行。
6.1.2 数据页
可以使用DBCC PAGE语句来查看数据页的内容,它允许查看数据库中任何指定页面的页面标题、数据行和行偏移量表。只有系统管理员才可以使用DBCC PAGE。但是,因为通常不需要查看数据页的内容,所以在SQL Server文档中找不到更多有关DBCC PAGE的内容。如果想使用它,其语法如下:
DBCC PAGE(| dbid | dbname | , filenum, pagenum[ , printopt ])
DBCC PAGE包括表6-2中所示的参数。图6-3显示的是DBCC PAGE的示例输出。需要注意的是,DBCC TRACEON(3604)将会指导SQL Server把结果返回给客户,而不是像很多处理内部问题的DBCC命令的默认情况那样将错误日志返回给客户。
参数 描述
dbid 包含页面的数据库的ID
dbname 包含页面的数据库的名称
filenum 包含页面的文件编号
fagenum 文件内的页数
printopt 可选的打印选项;选用其中一个值:
0:默认值,打印缓冲区的标题和页面标题
1:打印缓冲区的标题、页面标题(分别打印每一行),以及
行偏移量表
2:打印缓冲区的标题、页面标题(整体打印页面),以及行
偏移量表
3:打印缓冲区的标题、页面标题(分别打印每一行),以及行偏移量表;每一行后跟分别列出的它的列值
表6-2 DBCC PAGE 命令的参数
DBCC TRACEON(3604)
GO
DBCC PAGE(5, 1, 88, 1)
GO
PAGE: (1:88)
----------------
BUFFER:
----------
BUF@0×10EC4E80
---------------------
bpage=0×1B8A4000 bhash=0×00000000 bpageno = (1:88)
bdbid=5 breferences = 0 bstat = 0×9
bspin=0 bnext=0×00000000
PAGE HEADER:
----------------
Page @0×1B8A4000
--------------------
m_pageld= (1:88) m_headerVersion=1 m_type=1
m_typeFlagBits=0×0 m_level=0 m_fiagBits=0×0
m_objid=1977058079 m_indexid=0 m_prevPage=(0:0)
m_nextPage=(0:0) pminlen=24 m_slotCnt=23
m_freeCnt=6010 m_freeData=2136 m_reservedCnt=0
m_isn=(3:243:2) m_xactReserved=0 m_xdesld=(0:0)
m_ghostRccCnt=0 m_tornBits= -2147483591
Allocation Status
---------------------
GAM(1:2)=ALLOCATED SGAM(1:3)=NOT ALLOCATED
PFS(1:10=0×60 MIXED_EXT ALLOCATED 0_PCT_FULL DIFF(1:6)=CHANGED
ML(1:7)=NOT MIN_LOGGED
DATA
------
Slot 0 , Offset 0×631
----------------------------
Record Type=PRIMARY_RECORD
Record Attributes= NULL_BITMAP VARIABLE_COLUMNS
1b8a4631: 00180030 20383034 2d363934 33323237 0…408 496-7223
1b8a4641: 34394143 01353230 00000009 00330005 CA94025……….3
1b8a4651: 003f0038 0058004e 2d323731 312d3233 8.?.N.X.172-32-1
1b8a4661: 57363731 65746968 6e686f4a 316e6f73 176WhiteJohnson1
1b8a4671: 32333930 67694220 52206567 654d2e64 0932 Bigge Rd.Me
1b8a4681: 206f6c6e 6b726150 nlo Park
Slot 1, Offset 0×b8
--------------------------------
Record Type=PRIMARY_RECORD
Record Attributes= NULL_BITMAP VARIABLE_COLUMNS
1b8a40b8: 00180030 20353134 2d363839 30323037 0…415 986-7020
1b8a40c8: 34394143 01383136 00000009 00330005 CA94618………3
1b8a40d8: 00400038 00580051 2d333132 382d3634 8.@.Q. ×.213-46-8
1b8a40e8: 47353139 6e656572 6a72614d 6569726f 915GreenMarjorie
1b8a40f8: 20393033 64723336 2e745320 31342320 309 63rd St. #41
1b8a4108: 6b614f31 646e616c 1Oakland
Slot 2 , Offset 0×110
-----------------------------
Record Type=PRIMARY_RECORD
Rexord Attributes= NULL_BITMAP VARIABLE_COLUMNS
1b8a4110: 00180030 20353134 2d383435 33323737 0…415 548-7723
1b8a4120: 34394143 01353037 00000009 00330005 CA94705………3
1b8a4130: 003f0039 0055004d 2d383332 372d3539 9.?M.U.238-95-7
1b8a4140: 43363637 6f737261 6568436e 356c7972 766CarsonCheryl5
1b8a4150: 44203938 69777261 6e4c206e 7265422e 89 Darwin Ln.Ber
1b8a4160: 656c656b 79 keley
/* Data for slots 3 through 20 not shown */
Slot 21, Offset 0×1c0
----------------------------
Record Type = PRIMARY_RECORD
Record Attributes = NULL_BITMAP VARIABLE_COLUMNS
1b8a41c0:00180030 20313038 2d363238 32353730 0…801 826-0752
1b8a41d0:34385455 01323531 00000009 00330005 UT84152………3
1b8a41e0: 003d0039 0059004b 2d393938 322d3634 9.=.K.Y.899-46-2
1b8a41f0: 52353330 65676e69 6e6e4172 20373665 035RingerAnne67
1b8a4200: 65766553 2068746e 532e7641 20746c61 Seventh Av.Salt
1b8a4210: 656b614c 74694320 79 Lake City
Slot 22 , Offset 0×165
-----------------------------
Record Type =PRIMARY_RECORD
Record Attributes = NULL_BITMAP VARIABLE_COLUMNS
1b8a4165: 00180030 20313038 2d363238 32353730 0…801 826-0752
1b8a4175: 34385455 01323531 00000009 00330005 UT84152………3
1b8a4185: 003f0039 005b004d 2d383939 332d3237 9.?.M.[.998-72-3
1b8a4195: 53273635 65676e69 626c4172 36747265 567RingerAibert6
1b8a41a5: 65532037 746e6576 76412068 6c61532e 7 Seventh AV. Sal
1b8a41b5: 614c2074 4320656b 797469 t Lake City
OFFSET TABLE:
---------------------
Row _Offset
22(0×16) -357(0×165)
21(0×15) -448(0×1c0)
20(0×14) -711(0×2c7)
19(0×13) -1767(0×6e7)
18(0×12) -619(0×26b)
17(0×11) -970(0×3ca)
16(0×10) -1055(0×41f)
15(0×f) -796(0×31c)
14(0×e) -537(0×219)
13(0×d) -1673(0×689)
12(0×c) -1226(0×4ca)
11(0×b) -1949(0×79d)
10(0×a) -1488(0×5do)
9(0×9) -1854(0×73e)
8(0×8) -1407(0×57f)
7(0×7) -1144(0×478)
6(0×6) -96(0×60)
5(0×5) -2047(0×7ff)
4(0×4) -884(0×374)
3(0×3) –1314(0×522)
2(0×2) -272(0×110)
1(0×1) –184(0×b8)
0(0×) -1585(0×631)
图6-3 DBCC PAGE的示例输出
正如所看到的,DBCC PAGE的输出分为4个主要部分:BUFFER、PAGEHEADER、DATA,以及OFFSET TABLE(真正的行偏移量数组)。BUFFER部分显示的是关于给定页面的缓冲区的信息。(这里的“缓冲区”是管理页面的内存结构。)
图6-3中的PAGE HEADER部分显示的是页面中所有标题字段的数据。(表6-1显示的是大多数这些字段的意义。)DATA部分包含每一行的信息。对于每一行,DBCC PAGE指出行在槽中的位置,以及该行在页面中的偏移量。然后,页面数据被分成三部分。左列指出的是所显示的数据在行中的字节位置。接下来的4列包含页面中存储的实际数据,以十六进制的形式显示。右列包含数据的字符表示。在该列中,虽然可能会显示出一些其他数据,但只有字符数据是可读的。
OFFSET TABLE部分显示的是页面末尾的行偏移量数组的内容。在图6-3中可以看到,该页面包含23行,其中第一行(以槽0表示)是以偏移量1585(0×631)开始的。页面中物理存储的第1行实际上是第6行,在行偏移量数组中具有偏移量96。DBCC PAGE以槽的编号顺序来显示行,即使如此,正如通过每个槽偏移量所看到的,这并不是页面中行物理存在的顺序。