原文地址:http://rusanu.com/2012/07/27/how-to-shrink-the-sql-server-log/ html
说明:本文为了更好的说明收缩的过程,在原文翻译的基础上增长了一些我的的理解,省略了部份内容,建议你们在阅读本文时参考原文。 sql
1、问题场景 |
个人数据库日志文件已经增大到200G了,我也尝试去收缩数据库,但大小没有改变,请问该如何减少日志文件的大小?这个问题实际上就是说执行DBCC SHRINKFILE没有减少日志文件的大小,究竟是什么缘由致使的呢? 数据库
2、准备知识 |
一、LSN |
LSN用来标识特定日志在日志文件中位置(详情请见什么是LSN:日志序列号),它由两部分组成:一部分用来标识VLF(虚拟日志文件)的序列号,剩下的用来标识该日志在VLF中的具体的位置。 架构
根据LSN不一样,日志通常分为两类:首日志(最新的活动日志序号)和尾日志(保留时间最长的活动日志序号)。随着数据库的操做不断增长(如数据库中的update操做),首日志LSN序号不断变化。尾日志的序号只有在日志备份后才会变化。 spa
(图一)日志文件结构图操作系统
二、VLF |
你能够经过DCC LOGINFO去分析数据库LDF中VLF(虚拟日志文件),LDF、VLF、日志的关系是:LDF包括多个VLF,每一个VLF中包括多个日志记录。在VLF中,当事物日志增长时,日志的头部(首日志)不断向前移动,日志将占用愈来愈多的剩余空间,当这个VLF被占满后,新的日志写入到其余未被使用的VLF中,这个时候LDF并不会增大。当LDF中没有可用的VLF时,数据库会建立一个新的VLF。从而使得LDF文件物理增大,占用更多的磁盘空间。 翻译
(图二)日志增加3d
3、解决方法详细阐述 |
|
一、日志的截断 |
上图演示了首日志向前移动的场景,结合图一和图二能够看到,当VLF2的空间被日志填满后,数据库扩充LDF文件(向操做系统申请更多的磁盘空间),并在扩充后的LDF中新建一个VLF3用来填充新的日志记录。尽管VLF1中存在剩余空间,但由于VLF1中存在活动日志(哪怕只有一条),因此数据库没法利用这个VLF的剩余空间,(详细缘由能够参考这篇文章什么是LSN:日志序列号)。 代理
这个时候作日志备份就会发生日志截断的现象。通常会将截断理解为"删除"一些日志记录(非活动),实际上它只是意味着尾日志的向前移动:尾日志序号会被刷新成最小的活动日志序号,而从原来尾日志的位置到新位置之间的空间被标记为"可从新利用"。这个过程并不会减小LDF已占用的磁盘空间。以下图,整个VLF1的和部分VLF2上的日志(非活动)被截断了。 日志
日志截断示意图(图三)
随着事务日志不断增长,VLF3中日志头部所在的位置将不断向前移动,当VLF3的空间被占满后,数据库会从新利用VLF1的空间,这种写入、截断、再写入的方式造成一个写日志的循环。在此期间LDF并不会物理上增大。
日志循环使用示意图(图四)
二、为何日志不能收缩 |
如今咱们再来看一个日志没法收缩的场景:
图四中,VLF1中的日志不断增长,直到VLF1的全部空间都被填满(如图五),此时由于没有发生截断,尾日志都在VLF2上,且VLF2和VLF3都被标记为不可从新利用,数据库只能扩充LDF、新建一个VLF4用来记录新的日志,首日志的位置将出如今VLF4中,整个写日志的(从图一到图四)顺序为VLF2——>VLF3——>VLF1——>VLF4。这个过程会致使数据库的日志文件在物理上增大。
日志增加示意图(图五)
这时咱们再来截断事物日志,如上文所说,尾日志的会被更新,最后可能出现尾日志和首日志在同一个VLF上的场景。从日志文件记录的架构上来看,咱们能够将这个过程简单地理解为:截断的顺序会按照首日志移动的顺序移动,从VLF2——>VLF3——>VLF1——>VLF4,最终尾日志和首日志出如今同一个VLF上。
日志截断示意图二(图六)
如上图,这个LDF文件包括3个空的和1个只有小部分活动日志的VLF文件,首日志和尾日志在同一个VLF中,这种状况下,试图经过DBCC SHRINKFILE是不会减少LDF文件的大小的。
日志文件能被收缩的缘由是该文件尾部的数据被清除了,使得该部分空间被释放,而不是逃过尾部去删除文件首部或者中间部分的内容。这点与MDF文件不一样,MDF文件中的数据是不能被删除的,只能将文件尾部的数据迁移到其余区域的剩余空间上,而后释放尾部占用的空间。
在LDF中 ,日志是不能被迁移的,并且也没有迁移的必要,由于当事物被提交后,日志变为不活动状态,经过事物日志备份便可将其截断(特殊状况下日志备份不必定能截断,如发布订阅的环境)。
综上所述,日志文件能被收缩的前提是:日志文件的最后一个VLF必须是free状态,从后向前推,只要是free状态的VLF都会被收缩,据此能够估算一个日志文件能够释放的空间大小。
以下咱们看一个实际的例子:
USE DBname
DBCC loginfo
VLF状态示意图(图七)
从上图能够看到,这个数据库的日志文件共有13个VLF,其中有前12个处于free状态,最后1个处于活动状态,所以,咱们能够推断首日志和尾日志的位置都在这个VLF上。这个时候执行文件收缩将看不到文件减少的效果。
三、如何解决这个问题 |
那么碰到这种状况,该怎么去收缩日志呢:尽量多的执行一些可以产生大量日志的操做,这些日志将致使数据库从新利用startoffset靠前的非活动状态的VLF,将首日志的位置定位到这个startoffset,而后作一次事务日志备份,将尾日志也迁移到startoffset靠前的非活动状态的VLF中,以下图,最后再执行DBCC SHRINKFILE便可收缩日志文件。
日志截断示意图三(图六)
4、重要说明 |
前文中一直在说经过日志备份便可解决日志截断的问题,其实这只是最简单的场景。在实际环境中可能有不少因素会影响日志的截断,如:
日志备份只能截断非活动的日志,若是一个事物长时间运行,此时备份事物日志将不会引发截断发生。
事物日志分发中,只有当日志读取器代理已经读取完待分发的日志后,日志才能变得非活动状态。(以前我处理过一个相似问题,你们能够经过这个连接看看http://www.cnblogs.com/i6first/p/3281437.html。)
这两种数据库技术都须要将日志传递到接受端,在传递尚未完成时,日志会一直保留,即便是备份日志也没法截断。