原文地址:http://rusanu.com/2012/07/27/how-to-shrink-the-sql-server-log/

说明:本文为了更好的说明收缩的过程,在原文翻译的基础上增加了一些个人的理解,省略了部分内容,建议大家在阅读本文时参考原文。

一、问题场景

我的数据库日志文件已经增大到200G了,我也尝试去收缩数据库,但大小没有改变,请问该如何减小日志文件的大小?这个问题实际上就是说执行DBCC SHRINKFILE没有减小日志文件的大小,到底是什么原因导致的呢?

二、准备知识

1、LSN

LSN用来标识特定日志在日志文件中位置(详情请见什么是LSN:日志序列号),它由两部分组成:一部分用来标识VLF(虚拟日志文件)的序列号,剩下的用来标识该日志在VLF中的具体的位置。

根据LSN不同,日志一般分为两类:首日志(最新的活动日志序号)和尾日志(保留时间最长的活动日志序号)。随着数据库的操作不断增加(如数据库中的update操作),首日志LSN序号不断变化。尾日志的序号只有在日志备份后才会变化。

(图一)日志文件结构图

2、VLF

你可以通过DCC LOGINFO去分析数据库LDF中VLF(虚拟日志文件),LDF、VLF、日志的关系是:LDF包括多个VLF,每个VLF中包括多个日志记录。在VLF中,当事物日志增加时,日志的头部(首日志)不断向前移动,日志将占用越来越多的剩余空间,当这个VLF被占满后,新的日志写入到其他未被使用的VLF中,这个时候LDF并不会增大。当LDF中没有可用的VLF时,数据库会创建一个新的VLF。从而使得LDF文件物理增大,占用更多的磁盘空间。

(图二)日志增长

三、解决方法详细阐述

1、日志的截断

上图演示了首日志向前移动的场景,结合图一和图二可以看到,当VLF2的空间被日志填满后,数据库扩充LDF文件(向操作系统申请更多的磁盘空间),并在扩充后的LDF中新建一个VLF3用来填充新的日志记录。尽管VLF1中存在剩余空间,但因为VLF1中存在活动日志(哪怕只有一条),所以数据库无法利用这个VLF的剩余空间,(详细原因可以参考这篇文章什么是LSN:日志序列号)。

这个时候做日志备份就会发生日志截断的现象。一般会将截断理解为"删除"一些日志记录(非活动),实际上它只是意味着尾日志的向前移动:尾日志序号会被刷新成最小的活动日志序号,而从原来尾日志的位置到新位置之间的空间被标记为"可重新利用"。这个过程并不会减少LDF已占用的磁盘空间。如下图,整个VLF1的和部分VLF2上的日志(非活动)被截断了。

日志截断示意图(图三)

随着事务日志不断增加,VLF3中日志头部所在的位置将不断向前移动,当VLF3的空间被占满后,数据库会重新利用VLF1的空间,这种写入、截断、再写入的方式形成一个写日志的循环。在此期间LDF并不会物理上增大。

日志循环使用示意图(图四)

2、为什么日志不能收缩

现在我们再来看一个日志无法收缩的场景:

图四中,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上。这个时候执行文件收缩将看不到文件减小的效果。

3、如何解决这个问题

那么碰到这种情况,该怎么去收缩日志呢:尽可能多的执行一些能够产生大量日志的操作,这些日志将导致数据库重新利用startoffset靠前的非活动状态的VLF,将首日志的位置定位到这个startoffset,然后做一次事务日志备份,将尾日志也迁移到startoffset靠前的非活动状态的VLF中,如下图,最后再执行DBCC SHRINKFILE即可收缩日志文件。

日志截断示意图三(图六)

四、重要说明

前文中一直在说通过日志备份即可解决日志截断的问题,其实这只是最简单的场景。在实际环境中可能有很多因素会影响日志的截断,如:

  • 活动的事物日志

日志备份只能截断非活动的日志,如果一个事物长时间运行,此时备份事物日志将不会引起截断发生。

  • 事物日志分发

事物日志分发中,只有当日志读取器代理已经读取完待分发的日志后,日志才能变得非活动状态。(之前我处理过一个类似问题,大家可以通过这个链接看看http://www.cnblogs.com/i6first/p/3281437.html。)

  • 数据库镜像和AlwaysOn

这两种数据库技术都需要将日志传递到接受端,在传递还没有完成时,日志会一直保留,即使是备份日志也无法截断。

收缩SQL Server日志不是那么简单的(翻译)的更多相关文章

  1. 收缩SQL Server日志不是那么简单

    收缩SQL Server日志不是那么简单的(翻译)   原文地址:http://rusanu.com/2012/07/27/how-to-shrink-the-sql-server-log/ 说明:本 ...

  2. SQL Server日志文件庞大收缩方法(实测好用)

    原文:SQL Server日志文件庞大收缩方法(实测好用) 这两个命令连续执行,间隔时间越少越明显(可多次运行),直到达到效果 --截断 BACKUP LOG CloudMonitor TO DISK ...

  3. 解决Sql Server 日志满了,设置收缩

    解决Sql Server 日志满了,设置收缩: --查看文件占用空间 . '文件大小(MB)',* from sysfiles; ALTER DATABASE SpyData SET RECOVERY ...

  4. 清理SQL Server日志释放文件空间的终极方法

    清理SQL Server日志释放文件空间的终极方法  转自:http://www.cnblogs.com/dudu/archive/2013/04/10/3011416.html [问题场景]有一个数 ...

  5. SQL Server日志文件过大 大日志文件清理方法 不分离数据库

    SQL Server日志文件过大    大日志文件清理方法 ,网上提供了很多分离数据库——〉删除日志文件-〉附加数据库 的方法,此方法风险太大,过程也比较久,有时候也会出现分离不成功的现象.下面的方式 ...

  6. 收缩SQL数据库日志文件

    收缩SQL数据库日志文件 介绍具体的操作方法前,先说下我操作的实际环境和当时的状况.我的服务器是windows server 2008 R2 64位英文版,数据库是SQL server 2008英文版 ...

  7. SQL Server 日志和代理的错误日志

    本文介绍的日志不是事务日志,而是SQL Server 日志和代理的错误日志,按照主体把错误日志分为SQL Server.SQL Server Agent.Database Mail,以及 Window ...

  8. SQL SERVER 日志写入原理浅析

    昨天看到网上有一个关于SQL SERVER 课件,便随手下载了下来看看主要讲了些什么内容,于是看到了下面两个PPT页面 由于第一张PPT上的内容不太准确(日志文件中没有“日志页”的概念,只有VLF的概 ...

  9. sql server日志传送实践(基于server 2008 R2)

    SQL Server 2008 R2 主从数据库同步 相关参考:http://blog.itpub.net/30126024/viewspace-2639526/ sql server日志传送(基于s ...

随机推荐

  1. ERROR SparkUncaughtExceptionHandler: Uncaught exception in thread

    ERROR SparkUncaughtExceptionHandler: Uncaught exception in thread Thread[appclient-registration-retr ...

  2. [纯小白学习OpenCV系列]官方例程00:世界观与方法论

    2015-11-11 ----------------------------------------------------------------------------------- 其实,写博 ...

  3. VC++ MFC 按钮的全部样式Style

    Button Styles BS_3STATE 与复选框一样本样式按钮可被单击变暗.变暗状态通常用于指示本样式的按键正处于禁用状态. BS_AUTO3STATE   与三状态的复选框一样当用户选中它本 ...

  4. APK动态加载框架(DL)解析

    转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/39937639 (来自singwhatiwanna的csdn博客) 前言 好久 ...

  5. laravel 数据库迁移

    问题:之前有创建迁移文件 并且执行过 如果删除迁移文件 再重新创建迁移文件时就有问题 提示找不到之前的迁移文件 /** 一开始执行的命令 php artisan make:migration crea ...

  6. 开发板通过UART向主机发送数据

    /********************************* 代码功能:开发板通过UART向主机发送数据 使用函数: Serial.begin(数据传输的波特率); Serial.printl ...

  7. HDU 3974 Assign the task(dfs建树+线段树)

    题目大意:公司里有一些员工及对应的上级,给出一些员工的关系,分配给某员工任务后,其和其所有下属都会进行这项任务.输入T表示分配新的任务, 输入C表示查询某员工的任务.本题的难度在于建树,一开始百思不得 ...

  8. Block的copy和循环引用的问题

    在实际开发中,发现使用Block有着比delegate和notification更简洁的优势.于是在目前的项目中大量的使用block. 在我的头文件我是这样声明使用block的. 我将block声明为 ...

  9. 第一章 JacksonUtil 序列化与反序列化属性总结

    1.json-lib与Jackson 关于json-lib与Jackson对比总结如下: 1).性能方面,Jackson的处理能力高出Json-lib10倍左右. 2).json-lib已经停止更新, ...

  10. Python使用中文注释和输出中文(原创)

    刚开始学习python,需要在Python中注释中文和输出中文,现在开始尝试: 仅为初步学习参考,高手请绕行. -------------------------------------------- ...