代码改变世界

SQL Server事务日志管理的阶段,1级:事务日志概述

2017-12-12 16:03  斗转星移17  阅读(450)  评论(0编辑  收藏  举报

SQL Server事务日志管理的阶段,1级:事务日志概述

作者:Tony Davis,2013年10月30日(第一版:2011/06/17)
该系列

本文是SQL Server中“Stairway系列:事务日志管理的阶梯”的一部分

当事情进展顺利的时候,没有必要特别意识到事务日志的作用或工作原理。你只需要确信每个数据库都有正确的备份机制。当事情出错时,对事务日志的理解对于采取纠正措施是非常重要的,特别是在需要时间点恢复数据库的情况下,迫切需要!托尼·戴维斯(Tony Davis)给出了每个DBA应该知道的正确的细节级别。
级别1:事务日志概述

事务日志是一个文件,SQL Server在该文件中存储与日志文件关联的数据库上执行的所有事务和数据修改的记录。如果发生导致SQL Server意外关闭(如实例或硬件故障)的灾难,事务日志将用于恢复数据库,并保证数据的完整性。重新启动后,数据库将进入恢复过程,在该过程中将读取事务日志以确保将所有有效的已提交数据写入数据文件(前滚),并取消(回滚)任何部分未提交的事务。简而言之,事务日志是SQL Server确保数据库完整性和事务的ACID属性,特别是耐久性的基本手段。

DBA在管理事务日志方面的一些最重要的职责如下:

    选择正确的恢复模式 - SQL Server提供了三种数据库恢复模式:FULL(默认),SIMPLE和BULK LOGGED。 DBA必须根据数据库的业务需求选择合适的模型,然后建立适合该模式的维护程序。
    执行事务日志备份 - 除非以SIMPLE模式工作,否则DBA定期备份事务日志至关重要。一旦捕获到备份文件中,日志记录可以随后应用到完整的数据库备份,以便执行数据库恢复,并重新创建数据库,例如在发生故障之前的时间点。
    监视和管理日志增长 - 在繁忙的数据库中,事务日志的大小可以快速增长。如果不经常备份,或者如果大小不合适或分配了不正确的增长特性,事务日志文件可能会被填满,导致臭名昭着的“9002”(事务日志已满)错误,从而使SQL Server进入“只读”模式(或进入“资源暂挂”模式,如果在恢复过程中发生的话)。
    优化日志吞吐量和可用性 - 除了进行基本维护(如进行备份)之外,DBA还必须采取措施确保事务日志具有足够的性能。这包括硬件方面的考虑,以及避免可能影响事务性能的日志碎片等情况

在这个阶梯系列中,我们将详细考虑这些核心维护任务。在这里,在第一级,我们将首先概述SQL Server如何使用事务日志,以及影响DBA生命周期的两个最重要的方式,即数据库恢复和恢复以及磁盘空间管理
SQL Server如何使用事务日志

在SQL Server中,事务日志是一个物理文件,传统上由扩展LDF强制标识,尽管不是强制的。它是在创建数据库时自动创建的,以及通常由MDF扩展标识的主数据文件,但是也可以使用任何扩展来存储数据库对象和数据本身。事务日志虽然通常作为单个物理文件来实现,但也可以作为一组文件来实现。但是,即使在后一种情况下,它仍然被SQL Server视为单个顺序文件,因此,SQL Server不能也不会并行地写入多个日志文件,因此没有性能优势将事务日志实现为多个文件。这在第7级 - 调整和增长事务日志中有更详细的讨论。

每当T-SQL代码对数据库对象(DDL)或其所包含的数据进行更改时,不仅数据文件中的数据或对象已更新,而且更改的详细信息也将记录为事务中的日志记录登录。每个日志记录都包含有关执行更改的事务标识的详细信息,该事务开始和结束时,哪些页面发生了更改,发生了哪些数据更改等等。

注意:事务日志不是审计跟踪。它不提供对数据库所做更改的审计跟踪;它不保留对数据库执行的命令的记录,也不保存数据如何改变。

当进行数据修改时,希望从数据缓存中读取相关数据页面,或者如果它们不在缓存中,则首先从磁盘中检索相关数据页面。在数据缓存中修改数据,并在日志缓存中创建描述事务效果的日志记录。提交事务时,日志记录将写入磁盘上的事务日志。但是,实际更改的数据可能不会写入磁盘,直到稍后发生数据库检查点。高速缓存中自从从磁盘读取以来已经被修改的任何页面,以便高速缓存中的数据值与磁盘上的数据值不同被称为脏页面。这些脏页面可能包含两个:

    已经提交并“硬化”到事务日志文件但还没有到数据文件的数据
    由未决事务修改的数据,即尚未提交(或回滚)的数据

定期数据库检查点进程扫描数据高速缓存并将所有脏页面刷新到磁盘,此时修改会反映在物理数据文件以及日志文件中。即使在交易仍然开放的情况下,也会发生这种情况。在检查点期间,与打开的事务相关的脏页被刷新到磁盘,SQL Server始终确保与这些打开的事务相关的日志记录在将脏页刷新到数据文件之前从日志高速缓存刷新到事务日志文件。

注意:扫描数据高速缓存LazyWriter的另一个进程也可能会在检查点之外将脏数据页写入磁盘,如果强制内存压力这样做的话。

这里需要注意的重要一点是,日志缓冲区管理器始终保证在将数据页面写入物理数据文件之前,将更改描述(日志记录)写入到磁盘上的事务日志中。这种机制被称为预写日志。它本质上是SQL Server确保事务耐久性的机制(请参阅数据库事务的ACID属性)。

通过总是首先将更改写入日志文件,SQL Server具有一个机制的基础,该机制可以保证所有提交的事务的效果最终将反映在数据文件中,并且磁盘上源于未完成事务的任何数据修改,即那些既没有COMMIT也没有ROLLBACK的数据文件最终没有反映在数据文件中。

例如,如果某个事务(T1)提交之后,但在将受影响的数据写入数据文件之前数据库发生崩溃,则在重新启动时,将启动数据库恢复过程,尝试协调事务日志文件的内容和数据文件。它将读取事务日志文件,并确保包含记录在日志文件中的事务T1的所有操作“前滚”(重做),以便它们反映在数据文件中。

同样,在数据库崩溃后,恢复过程将通过从日志文件中读取相关操作并对数据执行反向物理操作,从而“回滚”(撤消)数据库中与未提交事务关联的任何数据更改

以这种方式,SQL Server可以在发生崩溃的情况下将数据库返回到一致的状态。更一般地说,回滚(撤消)过程发生如果:

    ROLLBACK命令是针对显式事务发出的
    发生错误,并且XACT_ABORT已打开
    如果数据库检测到数据库与启动事务的客户端之间的通信中断。

在这种情况下,将读取与中断事务有关的日志记录,或者显式发出ROLLBACK命令的日志记录,并将更改回滚。通过这些方法,SQL Server可以确保与事务相关的所有操作都能成功,或者全部失败。因此,事务日志代表了SQL Server在正常的日常操作中确保数据一致性和完整性的基本手段之一。

然而,事务日志扮演了另一个至关重要的角色,它提供了一个机制,在发生灾难的情况下,数据库可以恢复到以前的时间点。通过适当的规划和管理,您可以使用这些日志文件的备份来恢复所有数据,直至其损坏或无法使用。

事务日志和数据库恢复

正如前面所讨论的,事务日志文件存储一系列日志记录,根据事务开始的时间顺序进行,这些日志记录提供了针对该数据库发出的修改和事务的历史记录。每个日志记录都包含有关执行更改的事务标识的详细信息,该事务开始和结束时,哪些页面发生了更改,发生了哪些数据更改等等。事务日志文件中的日志记录被组织成多个部分,称为虚拟日志文件(VLF) - 这些在第2层 - 事务日志体系结构中有更详细的介绍。

SQL Server的预写式日志记录机制保证在将修改后的数据本身写入数据文件之前,修改(即日志记录)的描述将写入VLF。因此,日志记录可能包含关闭(已提交)事务或未决(未提交)事务的详细信息,并且在每种情况下,由事务修改的数据可能已经写入数据文件,也可能没有写入数据文件,具体取决于没有发生检查点。

注意:通过定期将脏页从缓存刷新到磁盘,数据库检查点进程控制SQL Server在数据库恢复操作期间需要执行的工作量。如果SQL Server必须前滚大量与脏页有关的已提交事务的更改,则恢复过程可能非常漫长。

任何与打开的事务相关的日志记录在恢复期间可能需要用于回滚操作,并且将始终是所谓的活动VLF的一部分,并将始终保留在日志文件中。与关闭的交易有关的日志记录也将是活动的VLF的一部分,直到达到在整个VLF中没有与打开的交易相关联的日志记录为止的点,此时VLF变为非活动状态。

这些不活动的VLF中的日志记录基本上提供了先前完成的数据库事务的“历史记录”,这些不活动的VLF会发生什么情况取决于数据库的恢复模式。我们将在这个阶梯的第3 - 6级详细讨论这些恢复模型,但是关键的一点是,如果您使用FULL(或BULK LOGGED)数据库恢复模式,那么事务日志将保留日志记录在不活动的VLF中,直到进行日志备份(稍后会详细介绍)。

通过备份事务日志,我们可以将实时日志中的所有日志记录(包括这些不活动的VLF中的日志记录)捕获到备份文件中。这些日志备份可以用来将数据库恢复到以前的时间点;希望有一个非常接近某个“灾难”发生点的时间点。在发生这种灾难的情况下,可以将日志备份文件应用到完整数据库备份文件的已还原副本,并且在数据库恢复过程中,完整备份后发生的任何事务将“前滚”,以恢复数据库并将数据恢复到给定的时间点,从而最大限度地减少数据丢失。当然,这里假定你不仅采取了这些日志备份,而且把它们转移到了一个安全的地方。如果日志备份文件与实时日志文件在同一个驱动器上,并且该驱动器崩溃,则可能会丢失所有备份。

当数据库处于SIMPLE恢复模式(更多关于级别3和4)时,活动VLF中的日志记录将被保留,因为它们可能是回滚操作所需的。但是,当检查点发生时,不活动的VLF将被截断,这意味着这些VLF中的日志记录可以立即被新的日志记录覆盖。这就是为什么在SIMPLE恢复中操作的数据库被称为处于自动截断模式。在此模式下,日志中不保留“历史记录”,因此无法在日志备份中捕获并作为还原过程的一部分使用。
控制日志文件的大小

希望前面的讨论清楚地表明,对于大多数在FULL恢复模式下运行的生产数据库,有必要对事务日志文件进行定期备份,以便使数据库能够恢复到特定的点时间。

但是,在FULL(或BULK_LOGGED)模式下运行这些日志备份还有一个重要原因,那就是控制日志的大小。请记住,日志记录将写入日志文件中,用于修改SQL Server数据库中的数据或对象的每个事务。
在繁忙的系统中,有许多并发事务或者写入大量数据的事务,事务日志的大小可能会很快增长。

在FULL(或BULK_LOGGED)模式下工作时,在备份文件中捕获不活动VLF中的日志记录副本是唯一可以使这些VLF符合截断条件的操作,这意味着日志记录占用的空间可用于重用。

关于截断和事务日志大小的简要说明:有一个常见的错误概念,即截断日志文件意味着日志记录被删除,文件大小减小。它不是;截断日志文件仅仅是将空间标记为可重用的行为。在每个不同的恢复模型的上下文中,将在后续的Levels中更详细地讨论截断。

因此,在FULL(或BULK_LOGGED)模式下执行常规事务日志备份至关重要的原因之一是控制日志的大小。
备份事务日志的简单示例

为了简要说明我们在第一级中讨论的一些概念,我们将通过一个非常简单的示例来备份在FULL恢复模式下运行的数据库的事务日志。每个单独的进程和命令的细节将在后续的级别中进行更详细的介绍。

在清单1.1中,我们在SQL Server 2008实例上创建一个新的TestDB数据库,然后使用DBCC SQLPERF(LOGSPACE)命令立即获得日志文件的大小。

 

清单1.1:新的TestDB数据库的初始日志文件大小

正如您所看到的,日志文件大小约为1 MB,约占满30%。

注意:实例上创建的用户数据库的初始大小和增长特征由模型数据库的属性决定,因为每个数据库将使用的默认恢复模型(在本例中为FULL)。我们将在第7级 - 调整和增长交易日志中更详细地讨论这些属性的影响。

文件的大小可以通过简单地将物理文件放在磁盘上来确认,如图1.1所示。

图1.1:TestDB的数据和日志文件

现在让我们为TestDB执行数据文件的备份,如清单1.2所示(首先需要创建“Backups”目录)。请注意,此备份操作可确保数据库真正在FULL恢复模式下运行;更多关于这个在3级 - 交易日志,备份和恢复。

- 数据库的完整备份
BACKUP DATABASE TestDB
TO DISK ='C:\ Backups \ TestDB.bak'
用INIT;


清单1.2:TestDB的初始完全备份

数据或日志文件的大小(由于此备份操作)或所用日志空间的百分比没有变化,因为目前还没有用户表或数据库中的数据,所以这可能并不令人吃惊。让我们把这个做好,在这个数据库上创建一个名为LogTest的表格,填充100万行数据,然后重新检查日志文件的大小,如清单1.3所示。经常在SQLServerCentral.com论坛上看到的这个脚本的作者是杰夫·莫德恩(Jeff Moden),并且在他的友好许可下被转载。不要担心代码的细节;这里唯一重要的是我们插入了很多行。此代码可能需要几秒钟才能在您的机器上执行,这不是因为代码效率低下;这些都是幕后工作,写入数据和日志文件。

使用TestDB;

如果OBJECT_ID('dbo.LogTest','U')不为NULL
    DROP TABLE dbo.LogTest;
- =====作者:Jeff Moden
- =====创建并填充1,000,000行测试表。
- “SomeID”的范围为1到1000000个唯一编号
- “SomeInt”的范围是1到50000个非唯一的数字
- “SomeLetters2”;“AA” - “ZZ”非唯一2字符串
- “一些钱”; 0.0000至99.9999非唯一编号
- “SomeDate”; > = 01/01/2000和<01/01/2010非唯一
- “SomeHex12”; 12个随机的十六进制字符(即0-9,A-F)
SELECT TOP 1000000
        SomeID = IDENTITY(INT,1,1),
        SomeInt = ABS(CHECKSUM(NEWID()))%50000 + 1,
        SomeLetters2 = CHAR(ABS(CHECKSUM(NEWID()))%26 + 65)
        + CHAR(ABS(CHECKSUM(NEWID()))%26 + 65),
        SomeMoney = CAST(ABS(CHECKSUM(NEWID()))%10000 / 100.0 AS货币),
        SomeDate = CAST(RAND(CHECKSUM(NEWID()))* 3653.0 + 36524.0 AS DATETIME),
        SomeHex12 = RIGHT(NEWID(),12)
INTO dbo.LogTest
从sys.all_columns ac1
        CROSS JOIN sys.all_columns ac2;
DBCC SQLPERF(LOGSPACE);

 

清单1.3:在TestDB中的LogTest表中插入一百万行

请注意,日志文件大小已经膨胀到将近110 MB,并且日志文件已满91%(这些数字可能与您的系统稍有不同)。如果我们要插入更多的数据,它将不得不再次增长以容纳更多的日志记录。同样,大小的增加可以从物理文件中确认(数据文件已经增长到64 MB)。

我们可以通过重新运行清单1.2来重新备份数据文件,这对日志文件的大小或文件中使用的空间的百分比没有影响。但是,现在让我们备份事务日志文件并重新检查这些值,如清单1.4所示。

- 现在备份事务日志
备份日志TestDB
TO DISK ='C:\ Backups \ TestDB_log.bak'
用INIT;

DBCC SQLPERF(LOGSPACE);
数据库名称日志大小(MB)使用的日志空间(%)状态
-------------------------------------------------- -----
大师1.242188 63.52201 0
... <剪断> ...
TestDB 99.74219 6.295527 0

清单1.4:备份TestDB的事务日志

日志文件仍然是相同的物理大小,但通过备份文件,SQL Server能够截断日志,使日志文件中的“非活动”VLF中的空间可供重用;可以添加更多日志记录,而不需要物理增长文件。另外,当然,我们已经将日志记录捕获到一个备份文件中,所以我们可以将该文件作为数据库恢复过程的一部分,如果需要将TestDB数据库恢复到之前的状态。
概要

在这第一级,我们介绍了事务日志,并解释了SQL Server如何通过预写式日志记录机制来保持数据一致性和完整性。我们还描述并简要地演示了DBA如何将事务日志文件的内容捕获到备份文件中,然后可以将其重用作恢复过程的一部分来恢复数据库。最后,我们强调了备份在控制事务日志大小方面的重要性。

在下一个层次中,我们将仔细研究事务日志的体系结构。
 
资源:
TLogStairway_Level1.sql

本文是SQL Server Stairway中“事务日志管理”阶段的一部分