浅谈SQL Server中的事务日志(一)----事务日志的物理和逻辑构架
简介
SQL Server中的事务日志无疑是SQL Server中最重要的部分之一。因为SQL SERVER利用事务日志来确保持久性(Durability)和事务回滚(Rollback)。从而还部分确保了事务的ACID属性.在SQL Server崩溃时,DBA还可以通过事务日志将数据恢复到指定的时间点。当SQL Server运转良好时,多了解一些事务日志的原理和概念显得并不是那么重要。但是,一旦SQL SERVER发生崩溃时,了解事务日志的原理和概念对于快速做出正确的决策来恢复数据显得尤为重要.本系列文章将会从事务日志的概念,原理,SQL Server如何使用日志来确保持久性属性等方面来谈SQL Server的事务日志.
事务日志的物理组织构架
事务日志仅仅是记录与其对应数据库上的事务行为和对数据库修改的日志文件.在你新建数据库时,伴随着数据库文件,会有一个默认以ldf为扩展名的事务日志文件. 当然,一个数据库也可以配有多个日志文件,但是在逻辑上,他们可以看成一个.
在SQL Server对于日志文件的管理,是将逻辑上一个ldf文件划分成多个逻辑上的虚拟日志文件(virtual log files,简称VLFs).以便于管理。用个类比方法来看,日志文件(ldf)好比一趟火车,每一节车厢都是一个虚拟日志文件(VLFs):
那为什么SQL Server要把日志文件划分出多个VLFS呢?因为SQL Server通过这种方式使得存储引擎管理事务日志更加有效.并且对于日志空间的重复利用也会更加高效。使用VLF作为收缩数据库的最小单位比使用ldf文件作为最小单位无疑是更加高效的.
VLFS的个数和大小无法通过配置进行设定,而是由SQL Server进行管理.当Create或Alter数据库时,SQL Server通过ldf文件的大小来决定VLFS的大小和数量。在日志文件增长时,SQL Server也会重新规划VLFS的数量.
注意:根据这个原理不难看书,如果设置日志文件的增量过小,则会产生过多的VLFS,也就是日志文件碎片,过多的日志文件碎片会拖累SQL Server性能.
SQL Server创建数据库时,根据日志文件(ldf)的大小,生成VLF的数量公式如下:
下面我们来看一个例子:
创建数据库,指定日志大小为65M
通过DBCC,我们可以看到,对应的有8个VLFs:
再次创建数据库,指定日志初始大小为28M:
可以看到,对应的,VLF的数量变为4:
而对于日志文件的增长,SQL Server使用了和创建数据库时相同的公式,也就是每次增长比如为2M,则按照公式每次增长4个VLFs.
我们创建一个TestGrow数据库,指定日志文件为2M,此时有4个VLFS:
当我们增长2M时,这个2M则是按照公式,再次分配4个VLFs:
此时,这时能看到的VLFs数量应该为4+4=8个:
由此可以看出,指定合适的日志文件初始大小和增长,是减少日志碎片最关键的部分.
事务日志的逻辑组织构架
当针对数据库对象所做的任何修改保存到数据库之前,相应的日志首先会被记录到日志文件。这个记录会被按照先后顺序记录到日志文件的逻辑末尾,并分配一个全局唯一的日志序列号(log sequence number,简称LSN),这个序列号完全是按照顺序来的,如果日志中两个序列号LSN2>LSN1,则说明LSN2所在LSN1之后发生的.
由此可以看出,将日志文件分为多个文件除了磁盘空间的考虑之外。完全不会像数据那样可以并行访问,所以将日志文件分为多个完全不会有性能上的提升.
LSN号可以看作是将日志文件和其记录数据之间的纽带.每一条日志不仅有LSN号,还有其对应事务的事务日志:
一个简单的图片示例如下:
许多类型的操作都记录在事务日志中。这些操作包括:
- 每个事务的开始和结束。
- 每次数据修改(插入、更新或删除)。这包括系统存储过程或数据定义语言 (DDL) 语句对包括系统表在内的任何表所做的更改。
- 每次分配或释放区和页。
- 创建或删除表或索引。
对于LSN如何在ROLLBACK或者是ROLL FORWARD中以及在备份恢复过程中起作用,会在后续文章中提到
总结
本篇文章从事务日志的逻辑和物理构架简单介绍了事务日志的构成.这是理解SQL Server如何利用日志保证持久性和数据备份恢复的基础。下一篇文章将会介绍SQL Server在操作中会如何使用到日志文件。
浅谈SQL Server中的事务日志(二)----事务日志在修改数据时的角色
简介
每一个SQL Server的数据库都会按照其修改数据(insert,update,delete)的顺序将对应的日志记录到日志文件.SQL Server使用了Write-Ahead logging技术来保证了事务日志的原子性和持久性.而这项技术不仅仅保证了ACID中的原子性(A)和持久性(D),还大大减少了IO操作,把对数据的修改提交到磁盘的工作交给lazy-writer和checkpoint.本文主要讲述了SQL Server修改数据时的过程以及相关的技术。
预写式日志(Write-Ahead Logging (WAL))
SQL Server使用了WAL来确保了事务的原子性和持久性.实际上,不光是SQL Server,基本上主流的关系数据库包括oracle,mysql,db2都使用了WAL技术.
WAL的核心思想是:在数据写入到数据库之前,先写入到日志.
因为对于数据的每笔修改都记录在日志中,所以将对于数据的修改实时写入到磁盘并没有太大意义,即使当SQL Server发生意外崩溃时,在恢复(recovery)过程中那些不该写入已经写入到磁盘的数据会被回滚(RollBack),而那些应该写入磁盘却没有写入的数据会被重做(Redo)。从而保证了持久性(Durability)
但WAL不仅仅是保证了原子性和持久性。还会提高性能.
硬盘是通过旋转来读取数据,通过WAL技术,每次提交的修改数据的事务并不会马上反映到数据库中,而是先记录到日志.在随后的CheckPoint和lazy Writer中一并提交,如果没有WAL技术则需要每次提交数据时写入数据库:
而使用WAL合并写入,会大大减少磁盘IO:
也许你会有疑问,那每次对于修改的数据还是会写入日志文件.同样消耗磁盘IO。上篇文章讲过,每一笔写入日志的记录都是按照先后顺序,给定顺序编号的LSN进行写入的,日志只会写入到日志文件的逻辑末端。而不像数据那样,可能会写到磁盘的各个地方.所以,写入日志的开销会比写入数据的开销小很多。
SQL Server修改数据的步骤
SQL Server对于数据的修改,会分为以下几个步骤顺序执行:
1.在SQL Server的缓冲区的日志中写入”Begin Tran”记录
2.在SQL Server的缓冲区的日志页写入要修改的信息
3.在SQL Server的缓冲区将要修改的数据写入数据页
4.在SQL Server的缓冲区的日志中写入”Commit”记录
5.将缓冲区的日志写入日志文件
6.发送确认信息(ACK)到客户端(SMSS,ODBC等)
可以看到,事务日志并不是一步步写入磁盘.而是首先写入缓冲区后,一次性写入日志到磁盘.这样既能在日志写入磁盘这块减少IO,还能保证日志LSN的顺序.
上面的步骤可以看出,即使事务已经到了Commit阶段,也仅仅只是把缓冲区的日志页写入日志,并没有把数据写入数据库.那将要修改的数据页写入数据库是在何时发生的呢?
Lazy Writer和CheckPoint
上面提到,SQL Server修改数据的步骤中并没有包含将数据实际写入到磁盘的过程.实际上,将缓冲区内的页写入到磁盘是通过两个过程中的一个实现:
这两个过程分别为:
1.CheckPoint
2.Lazy Writer
任何在缓冲区被修改的页都会被标记为“脏”页。将这个脏页写入到数据磁盘就是CheckPoint或者Lazy Writer的工作.
当事务遇到Commit时,仅仅是将缓冲区的所有日志页写入磁盘中的日志文件:
而直到Lazy Writer或CheckPoint时,才真正将缓冲区的数据页写入磁盘文件:
前面说过,日志文件中的LSN号是可以比较的,如果LSN2>LSN1,则说明LSN2的发生时间晚于LSN1的发生时间。CheckPoint或Lazy Writer通过将日志文件末尾的LSN号和缓冲区中数据文件的LSN进行对比,只有缓冲区内LSN号小于日志文件末尾的LSN号的数据才会被写入到磁盘中的数据库。因此确保了WAL(在数据写入到数据库之前,先写入日志)。
Lazy Writer和CheckPoint的区别
Lazy Writer和CheckPoint往往容易混淆。因为Lazy Writer和CheckPoint都是将缓冲区内的“脏”页写入到磁盘文件当中。但这也仅仅是他们唯一的相同点了。
Lazy Writer存在的目的是对缓冲区进行管理。当缓冲区达到某一临界值时,Lazy Writer会将缓冲区内的脏页存入磁盘文件中,而将未修改的页释放并回收资源。
而CheckPoint存在的意义是减少服务器的恢复时间(Recovery Time).CheckPoint就像他的名字指示的那样,是一个存档点.CheckPoint会定期发生.来将缓冲区内的“脏”页写入磁盘。但不像Lazy Writer,Checkpoint对SQL Server的内存管理毫无兴趣。所以CheckPoint也就意味着在这个点之前的所有修改都已经保存到了磁盘.这里要注意的是:CheckPoint会将所有缓冲区的脏页写入磁盘,不管脏页中的数据是否已经Commit。这意味着有可能已经写入磁盘的“脏页”会在之后回滚(RollBack).不过不用担心,如果数据回滚,SQL Server会将缓冲区内的页再次修改,并写入磁盘。
通过CheckPoint的运作机制可以看出,CheckPoint的间歇(Recovery Interval)长短有可能会对性能产生影响。这个CheckPoint的间歇是一个服务器级别的参数。可以通过sp_config进行配置,也可以在SSMS中进行配置:
恢复间歇的默认参数是0,意味着由SQL Server来管理这个回复间隔。而自己设置恢复间隔也是需要根据具体情况来进行界定。更短的恢复间歇意味这更短的恢复时间和更多的磁盘IO,而更长的恢复间歇则带来更少的磁盘IO占用和更长的恢复时间.
除了自动CheckPoint之外,CheckPoint还会发生在Alter DataBase以及关闭SQL Server服务器时。sysadmin和db_backupoperator组的成员以及db_owner也可以使用CheckPoint指令来手动保存CheckPoint:
通过指定CheckPoint后的参数,SQL Server会按照这个时间来完成CheckPoint过程,如果时间指定的短,则SQL Server会使用更多的资源优先完成CheckPoint过程。
通常情况下,将“脏”页写入磁盘的工作,Lazy Writer要做的比CheckPoint会多出许多。
总结
本文简单介绍了WAL的概念和修改数据库对象时,日志所扮演的角色。还分别介绍了CheckPoint和Lazy Writer,对于这些概念的理解是理解SQL Server DBA工作的基础。下篇文章将会讲述在简单恢复模式下日志的机制。
浅谈SQL Server中的事务日志(三)----在简单恢复模式下日志的角色
简介
在简单恢复模式下,日志文件的作用仅仅是保证了SQL Server事务的ACID属性。并不承担具体的恢复数据的角色。正如”简单”这个词的字面意思一样,数据的备份和恢复仅仅是依赖于手动备份和恢复.在开始文章之前,首先要了解SQL Server提供的几种不同备份类型。
SQL Server提供的几种备份类型
SQL Server所提供的几种备份类型基本可以分为以下三种(文件和文件组备份以及部分备份不在本文讨论之列):
1.完整(Full)备份:直接将所备份的数据的所有区(Extent)进行复制。这里值得注意的有2点:
- 完整备份并不像其名字“完整”那样备份所有部分,而是仅备份数据库本身,而不备份日志(虽然仅仅备份少量日志用于同步)
- 完整备份在备份期间,数据库是可用的。完整备份会记录开始备份时的MinLSN号,结束备份时的LSN号,将这个区间的日志进行备份,在恢复时应用到被恢复的数据库(这里经过修改,感谢魔君六道指出)
2.差异(Differential)备份:只备份上次完整备份后,做修改的部分。备份单位是区(Extent)。意味着某个区内即使只有一页做了变动,则在差异备份里会被体现.差异备份依靠一个BitMap进行维护,一个Bit对应一个区,自上次完整备份后,被修改的区会被置为1,而BitMap中被置为1对应的区会被差异备份所备份。而到下一次完整备份后,BitMap中所有的Bit都会被重置为0。
3.日志(Log)备份:仅仅备份自上次完整备份或日志备份之后的记录。在简单模式下,日志备份毫无意义(SQL Server不允许在简单恢复模式下备份日志),下文会说明在简单恢复模式下,为什么日志备份没有意义。
简单恢复模式(Simple Recovery Mode)
在简单恢复模式下,日志仅仅是为了保证SQL Server事务的ACID。并没有恢复数据的功能.
比如,我们有一个备份计划,如下:
我们在每周一0点做一次完整备份,在周三0点和周五0点分别做差异备份。在简单恢复模式下,如果周六数据库崩溃。我们的恢复计划只有根据周一0点的做的完整备份恢复后,再利用周五0点的差异备份进行恢复.而周五0点之后到服务器崩溃期间所有的数据将会丢失。
正如”简单”这个词所涵盖的意思,在简单恢复模式下,日志可以完全不用管理。而备份和恢复完全依赖于我们自己的完整和差异备份.
恢复模式是一个数据库级别的参数,可以通过在SSMS里或通过SQL语句进行配置:
简单恢复模式下日志的空间使用
在本系列文章的第一篇文章提到过,日志文件会划分成多个VLF进行管理,在逻辑上记录是线性的,给每个记录一个顺序的,唯一的LSN。
而在简单恢复模式下,为了保证事务的持久性,那些有可能回滚的数据会被写入日志。这些日志需要被暂时保存在日志以确保在特定条件下事务可以顺利回滚。这就涉及到了一个概念—最小恢复LSN(Minimum Recovery LSN(MinLSN) )
MinLsn是在还未结束的事务记录在日志中最小的LSN号,MinLSN是下列三者之一的最小值:
- CheckPoint的开始LSN
- 还未结束的事务在日志的最小LSN
- 尚未传递给分发数据库的最早的复制事务起点的 LSN.
下图是一个日志的片段:
(图片摘自MSDN)
可以看到,最新的LSN是148,147是CheckPoint,在这个CheckPoint之前事务1已经完成,而事务2还未完成,所以对应的MinLSN应该是事务2的开始,也就是142.
而从MinLSN到日志的逻辑结尾处,则称为活动日志(Active Log)。
而活动日志分布在物理VLF上的关系可以用下图表示:
因此,VLF的状态是源自其上所含有的LSN的状态,可以分为两大类:活动VLF和不活动VLF
而更加细分可以将VLF的状态分为以下四类:
1.活动(Active) –在VLF 上存储的任意一条LSN是活动的时,则VLF则为活动状态,即使一个200M的VLF只包含了一条LSN,如上图的VLF3
2.可恢复(Recoverable) – VLF是不活动的,VLF上不包含活动LSN,但还未被截断(truncated)
3.可重用(Reusable) – VLF是不活动的,VLF上不包含活动LSN,已经被截断(truncated),可以重用
4.未使用(Unused) – VLF是不活动的,并且还未被使用过
概念如下图:
而所谓的截断(truncated)只是将可恢复状态的VLF转换到可重用状态。在简单恢复模式下,每一次CheckPoint,都会去检查是否有日志可以截断.如果有inactive的VLF时,CheckPoint都会将可截断部分进行截断,并将MinLSN向后推.
在日志达到日志文件(ldf文件)末尾时,也就是上图的VLF8时,会重新循环到VLF1开始,以便让空间进行重复利用.所以日志虽然可以从物理顺序上是从VLF1到VLF8,但逻辑顺序可以是从VLF6开始到VLF2结束:
因此可以看出,简单恢复模式下日志是不保存的(当事务结束后,相关的会被截断)。仅仅是用于保证事务回滚和崩溃恢复的用途.所以备份日志也就无从谈起,更不能利用日志来恢复数据库。
总结
本文介绍了简单恢复模式下日志的原理,并简单的引出了一些备份或者恢复数据的基础。而实际上,除了在开发或测试环境下。使用简单恢复模式的场景并不多,因为在现实生活中,在生产环境允许几个小时的数据丢失的场景几乎没有.下篇文章将会讲述在完整恢复模式下,日志的作用。
|