4.2.4 SQL Server数据库文件
SQL Server将数据存储在数据文件中,将事务记录存储在事务□志文件中。如果通过 一个逻辑数据库名称将它们集合到一起,这些文件就是数据库。SQL Server数据库可以具 有多个数据文件和多个事务日志文件(虽然-个事务日志文件通常已经足够)。当首次创建数据库时,它会有一个默认文件扩展名为.mdf的主数据文件。数据库也可 以有默认扩展名为m df的辅助数据文件。这些数据文件可以组合到一个称为文件组的逻辑 组中,第 5 章将对此进行介绍。该数据库至少有一个默认扩展文件名为.ldf的事务日志文 件。SQL Server数据库的文件扩展名并不是强制的,因此可以使用任何扩展名,但我们通 常使用默认的扩展名,因为默认扩展名可以很容易地显示出文件的用途。下列几节只描述 数据文件和事务日志文件的物理存储结构。要全面了解数据库的创建过程及如何创建和使 用文件,可参阅第5 章。4 .2 .5 数据文件
数据库的主数据文件(.mdf)以及任何辅助数据文件(.ndf)有着相同的结构。这两种文件 用来存储数据,以及可让SQL Server有效地査找、读取、修改和添加数据到数据库的元数 据。来自表和索引的所有数据以及描述数据的元数据都组织在称为区和页的存储对象中。1.区区 是 SQL Server的一种文件存储结构,大小 为 64KB。区 由 8 个连续的8KB页构成。 区分为两种:混合区和统一区。混合区包含来自多个对象的页。例如,混合区可能包含来 自表A 的数据页、来 自 表 B 的索引页,还有更多来自表C 的数据贞。因为一个区中有8 个页,所 以 8 个不同的对象可以共享一个区。统一区包含8 个属于同一对象的连续页。它 们的差异如图4-1所示。当在数据库操作期间检索数据或将数据写入磁盘时,区是数据检索的基本结构。SQL Server总是以64KB的增量分配空间。这很好地对应了数据在内存和在NT文件系统(NTFS) 格式分区中的组织方式。但是,如前所述,SQL Server可在单个区中存储来自不同对象的 页,从而最大化存储效率。2.页
SQL Servei* 2008数据库的所有数据和元数据都存储在页中。和区不一样,页总是存储 来自同一对象的数据。这包括来自表的行、来自索引的行以及大型对象数据。页的大小为 8KB,并且组织在64KB的区(由8 个连续的8KB页组成)中。每个页有一个96字节的标头, 它含有页的信息,例如页码、存储在页上的数据类型,页上的可用空间,以及拥有该页的 对象。SQL Server包含了一些用来存储和管理数据的不同类型的页。数据页数据页包含来自表的数据行。这些行不能跨页。由于有页标头和行偏移量信息,最大 的行大小被限制为8060字节。行大小由行中的列数和定义于每列上的数据类型所决定。为 了最大限度地提髙性能,表和索引行应尽可能窄些。例如,如果一个表行的宽度是4 100 字节,那每个数据页上只能存储一行,留下了近4000字节的不可用空间。从这样一种结构 的表中读取结果的话,仅检索4100字节的数据就要求读取8KB数据。效率显然太低。物 理数据页结构如图4-2所示。
对于页上的每行,每个行偏移量块都占用2 个字节的空间。为了优化存储空间,表中
的行在物理上的排列不同于它们的逻辑定义。当一行存放在数据页上时,它是用一个4 字 节的标头(它可唯一标识页中的行)来标识的。标头后面为定长数据列、一个NULL块、一 个变长块,以及物理行末尾的所有变长数据列,如图4-3所示
NULL块包含一个2 字节的块,指明行中有多少列可包含NULL;后跟一个表明可空
列是否为空的位图。NULL位图的大小为每列1位,舍入至最接近的字节数。1〜 8 个可空 列需要一个1字节的位图。9〜 16个列需要一个2 字节的位图,依此类推。和 NULL块一样,变长块包含一个2 字节的块,指明可以有多少变长列;后跟一个表 明每个变长列的最大长度的位 图 。不同于NULL块的是,变长块位图的大小为每列2 个字 节,指向每个变长列的末尾,这样所有变长数据都可连续地存储在数据行的末尾。如果没 有列被定义为变长列,则变长块将被将略.索引页索引页包含来自索引的行。它们和数据页有着相同的结构及限制。文本/图像页当使用大型对象数据类型定义列时,SQL Server会在实际的数据行中放置一个16字节 的指针,并将大型对象数据放在单独的数据页上。这类数据包括被定义为text、ntext、image、 varchar(MAX)、nvarchar (MAX)和 varbinary (MAX)和 XML 的数据o全局分配映射表(GAM)和辅助全局分配映射表(SGAM)页GAM和 SGAM页是基于每个文件管理区的分配页。每个数据文件的第2 页是GAM 页,而第3 页是SGAM页。SQL Server会在必要时添加额外的GAM和 SGAM页,因为 每个GAM和 SGAM页只能跟踪63 904个区。GAM和 SGAM页构成了一个表明区是统一 区的还是混合区的位图。GAM和 SGAM位图也会指出区是满的、空的,或是还有可用数 据页。页可用空间(PFS)页PFS页记录每一页的状态、页是否已被分配,以及每页上的可用空间。索引分配映射(IAM)页IAM页包含表或者索引使用的区的信息。它包含一个对象的8 个初始页的位置信息, 以及一个表示用于该对象的区的位图。每 个 IAM 页最多可跟踪512 000个数据页。SQL Server利用IAM和 PFS页来为数据查找和分配新页。大容量更改映射表(BCM)页BCM页包含自上一次事务日志备份以来被人容量操作修改的区的位置。大容量操作包括 UPDATETEXT、WRITETEXT、SELECT INTO、BULK INSERT 以及图像操作。当数据
库处于大容量日志恢复模式时,BCM页主要是用于事务日志备份操作(第9 章详细介绍了 大容量日志恢复模式)。差异更改映射表(DCM)页DCM页包含自上次数据库备份以来被修改的区的标识符。执行差异备份时使用DCM页。4 . 2 . 6 事务日志
事务日志的作用是在一个特定的时间间隔内,维护发生在一个SQL Server数据库上的 所有事务的物理记录。具体时间间隔取决于数据库的恢复模式。在默认的数据库配置下,事务日志记录所有的数据库修改,并且除非是已经备份或者 被数据库管理员显式截断,这些记录是不会被删除的。事务日志是一个二进制文件。它不是一个可用日志査看器或者记事本打幵并査看的传 统日志文件,因此数据库管理员不能轻易査看到其内容。数据库管理员可以使用一些第三 方产品来打开并査看事务日志的内容。这些产品可用于审核数据库修改,并可用于创建改 正不希望发生的事务的脚本。事务日志作为一个或多个物理文件在磁盘上进行维护。大多数情况下,一个事务日志 文件就已足够,因为在第一个事务日志完全写满并达到其最大容量之前,不会使用其他任 何日志文件。在内部,物理事务日志文件分为多个虚拟日志。虚拟日志文件的数量和大小 将由SQL Server动态配置,因此是不可手动配置的。当 SQL Server配置事务日志的内部结 构时,它会尽量把虚拟事务日志的数量控制到最少。为帮助SQL Server维持数量较少的虚拟日志,事务日志的初始大小应设置为可容纳在 事务日志备份之间可能发生的所有预期事务。如果日志被配置为自动增长,应将增量设置 得较大些,这样可避免会导致创建多个小虚拟日志的微量重复性增长。1 .事务所有数据修改都在事务中发生并被记录在事务日志中。事务是一个可控的完整数据操 作单元,因此事务中的所有修改要么都发生,要么都不发生。SQL Server有 3 种执行事务 的方式:隐式事务、显式事务和自动提交事务。隐式事务和自动提交事务是互斥的。自动提交事务默认情况下,SQL Server连接使用自动提交事务。任何单独执行或批量执行的INSERT、 UPDATE或 DELETE语句将被自动应用于数据库。如下例所示:UPDATE CheckingAccount SET Balance =Balance +500 WHERE AccountID = 1123456789-CK* UPDATE SavingsAccount SET Balance =Balance - 500 WHERE AccountID =•123456789-SV,隐式事务
结构化査询语言的ANSI标准规定除非有显式提交,否则不能更改数据。SQL Server 通过一个 名 为 IMPLICIT_TRANSACTIONS的连接属性来支持这项规定。当 IMPLICIT^ TRANSACTIONS设置为ON吋,任何数据修改都将隐式开始一个事务,但不会关闭事务。 该事务将保持开启状态,直至被显式提交或冋滚。如下例所示:SET IMPLICITTRANSACTIONS ONBEGIN TRYUPDATE CheckingAccount SET Balance =Balance + 500 WHERE AccountID ='123456789-CK*UPDATE SavingsAccount SET Balance =Balance - 500 WHERE AccountID = * 123456789-SV1COMMIT TRANSACTIONEND TRYBEGIN CATCHROLLBACK TRANSACTIONRAISERROR(’Account Transfer Failed', 14,1)END CATCH此例中,如果在修改数据时有任何错误发生,则将调用CATCH块来回滚事务。如果 没有错误发生,该事务将被提交。在自动提交模式下,同样的逻辑是行不通的,因为没有 任何隐式或显式的事务提交或者回滚。打开IMPLICIT_TRANSACTIONS会关闭自动提交 模式。显式事务
显式事务需要用BEGIN TRANSACTION来幵始事务,用显式的COMMIT TRANSACTION 或者ROLLBACK TRANSACTION来关闭事务,如下面的代码所示:BEGIN TRYBEGIN TRANSACTIONUPDATE CheckingAccount SET Balance =Balance +500 WHERE AccountID =*123456789-CK1
UPDATE SavingsAccount SET Balance =Balance - 500 WHERE AccountID =*123456789-SV*COMMIT TRANSACTIONEND TRYBEGIN CATCHROLLBACK TRANSACTIONRAISERROR('Account Transfer Failed', 14,1}END CATCH在这个例子中,和之前的隐式事务一样,任何错误都会导致立即回滚事务以确保数据 的完整性。大量SQLServer的文档声明,事务是“必须完全成功或失败的单个工作单元”。不过, 即使数据修改都在事务中进行,这并不能保证该事务将全部完成。没有TRY和 CATCH块, 显式或隐式事务的行为与自动提交类似。任何成功的修改将应用于数据库,而失败的修改 则不会。正确进行错误处理是管理事务的关键。2 . 记录事务
在了解了事务的概念后,现在看SQL Server是如何把它们记录在磁盘上的。 直接对数据库中的数据文件进行数据修改是不可能的。当一个应用程序发送修改时, SQL Server会在缓冲区缓存中査找包含该数据的数据页,或者在插入的情况下,查找有足 够空间容纳数据的页。如果该页不在缓存中,SQL Server会从磁盘读取页,然后将它放置 在缓存中,并且在缓存中进行修改。同时,SQL Server把数据修改记录在磁盘的事务曰志 中。当页最初读入缓存时,它是一个“干净”的页。一旦事务修改了页,它就变“脏”了。SQL Server会定期发出一个名为CHECKPOINT的事件。当这个事件发出时,缓存中 的所有脏页都被写入到磁盘上的数据文件中。检査点的目的是为了减少存储在缓存中的脏 页,最小化SQL Server从失败中恢复的时间。考虑下列事件序列:BEGIN TRANSACTION 1UPDATEINSERTUPDATECOMMIT TRANSACTION 1BEGIN TRANSACTION 2INSERTUPDATE♦♦♦CHECKPOINT***
BEGIN TRANSACTION 3DELETEUPDATECOMMIT TRANSACTION 3 BEGIN TRANSACTION 4 UPDATE …***Server Power failure***当 SQL Server在电力故障后重新启动时,它会读取事务日志査找最后一次发出的 CHECKPOINT。从 最 后 一 个 CHECKPOINT到日志开始的所有内容都已安全写入磁盘。但
是,CHECKPOINT之后的数据修改只记录在事务□志中。因为事务3 成功提交,调用应用 程序会被通知提交成功,并且希望看到所有提交的修改。鉴于此,SQL Server会前滚整个 事 务 3 , 将更改提交到磁盘。另一方面,事 务 2 没有成功提交,虽然前两项修改已经由 CHECKPOINT写入磁盘。SQL Server将使用事务日志中的信息来撤消修改或者回滚修改。 事务4 也还没有成功提交,它也没有被写入磁 盘 。事 务 4 的数据修改将从事务曰志中删除。
3 .事务日志物理特征
事务日志是一种串行化的、顺序的、冋绕的日志。当把数据修改写入日志时,它们会 得到一个日志序列号(LSN)。由于事务日志用于记录越来越多的事务,它最终会被填满。 如果已将事务日志设置成自动增长(见第5 章),那么SQL Server将分配额外的文件空间以 容纳事务记录的存储。这种行为将持续下去,直至达到事务日志的最大容量,或者包含事 务日志的磁盘被填满。如果事务日志被完全填满,那么数据库将不会允许数据修改。为避免事务日志完全被填满,必须定期清除日志中的旧事务。首选的清除方式是备份事 务曰志(见第7 章)。默认情况下,一旦事务日志已经成功备份,SQL Server将会清除事务日 志的不活动部分。事务日志的不活动部分包括从最早打开的事务的LSN到最新的LSN。这 样清除事务日志不会减小事务日志的大小,但却为存放额外的事务记录腾出了空间。也可以手动清除事务日志的不活动部分,但不推荐这样做,因为这样做会删除上次数 据库备份以来的所有数据修改记录。如前所述,事务日志是一个回绕文件。一旦到达了物理日志的末尾,SQL Server将绕 冋并在物理日志的开头继续写当前逻辑日志,如图4-4所示。4 . 3 小结
本章讨论了 SQL Server如何在磁盘上物理存储数据和事务记录。虽然这些内容可能有 点深奥,但是随着您的SQL Server技能提髙,在遇到更复杂的、需要深入理解SQL Server 如何存储和检索数据的故障诊断和优化问题时,这些内容会很有用。记住,本章只是涉及 了数据库引擎的内部工作原理的一点皮毛。要全面了解数据库引擎的内部机制,请参考SQLServer 2008联机丛书。数据库是SQL Server的核心所在,第 5 章将讲述SQL Server用来管理、修改和组织存 储在数据库中的数据的所有组件,包括用来存储数据的表和用于修改数据的编程对象等。