设为首页 收藏本站
查看: 1076|回复: 0

[经验分享] Sql Server 收缩日志文件原理及always on 下的实践

[复制链接]

尚未签到

发表于 2017-12-14 13:33:30 | 显示全部楼层 |阅读模式
  一、准备知识
  1、LSN
  LSN用来标识特定日志在日志文件中位置(详情请见什么是LSN:日志序列号),它由两部分组成:一部分用来标识VLF(虚拟日志文件)的序列号,剩下的用来标识该日志在VLF中的具体的位置。
  根据LSN不同,日志一般分为两类:首日志(最新的活动日志序号)和尾日志(保留时间最长的活动日志序号)。随着数据库的操作不断增加(如数据库中的update操作),首日志LSN序号不断变化。尾日志的序号只有在日志备份后才会变化。
DSC0000.png

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

  (图二)日志增长
  二、解决方法详细阐述
  1、日志的截断
  上图演示了首日志向前移动的场景,结合图一和图二可以看到,当VLF2的空间被日志填满后,数据库扩充LDF文件(申请更多的磁盘空间),并在扩充后的LDF中新建一个VLF3用来填充新的日志记录。尽管VLF1中存在剩余空间,但因为VLF1中存在活动日志(哪怕只有一条),所以数据库无法利用这个VLF的剩余空间。
  这个时候做日志备份就会发生日志截断的现象。一般会将截断理解为"删除"一些日志记录(非活动),实际上它只是意味着尾日志的向前移动:尾日志序号会被刷新成最小的活动日志序号,而从原来尾日志的位置到新位置之间的空间被标记为"可重新利用"。这个过程并不会减少LDF已占用的磁盘空间。如下图,整个VLF1的和部分VLF2上的日志(非活动)被截断了。
DSC0002.png

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

  (图四)日志循环使用示意图
  2、为什么日志不能收缩
  现在我们再来看一个日志无法收缩的场景:
  图四中,VLF1中的日志不断增加,直到VLF1的所有空间都被填满(如图五),此时因为没有发生截断,尾日志都在VLF2上,且VLF2和VLF3都被标记为不可重新利用,数据库只能扩充LDF、新建一个VLF4用来记录新的日志,首日志的位置将出现在VLF4中,整个写日志的(从图一到图四)顺序为VLF2——>VLF3——>VLF1——>VLF4。这个过程会导致数据库的日志文件在物理上增大。
DSC0004.png

  (图五)日志增长示意图
  这时我们再来截断事物日志,如上文所说,尾日志的会被更新,最后可能出现尾日志和首日志在同一个VLF上的场景。从日志文件记录的架构上来看,我们可以将这个过程简单地理解为:截断的顺序会按照首日志移动的顺序移动,从VLF2——>VLF3——>VLF1——>VLF4,最终尾日志和首日志出现在同一个VLF上。
DSC0005.png

  (图六)日志截断示意图二
  如上图,这个LDF文件包括3个空的和1个只有小部分活动日志的VLF文件,首日志和尾日志在同一个VLF中,这种情况下,试图通过DBCC SHRINKFILE是不会减小LDF文件的大小的。
  日志文件能被收缩的原因是该文件尾部的数据被清除了,使得该部分空间被释放,而不是逃过尾部去删除文件首部或者中间部分的内容。这点与MDF文件不同,MDF文件中的数据是不能被删除的,只能将文件尾部的数据迁移到其他区域的剩余空间上,然后释放尾部占用的空间。
  在LDF中 ,日志是不能被迁移的,而且也没有迁移的必要,因为当事物被提交后,日志变为不活动状态,通过事物日志备份即可将其截断(特殊情况下日志备份不一定能截断,如发布订阅的环境)。
  综上所述,日志文件能被收缩的前提是:日志文件的最后一个VLF必须是free状态,从后向前推,只要是free状态的VLF都会被收缩,据此可以估算一个日志文件可以释放的空间大小。
  如下我们看一个实际的例子:
  USE DBname
  DBCC loginfo
DSC0006.png

  (图七)VLF状态示意图
  从上图可以看到,这个数据库的日志文件共有13个VLF,其中有前12个处于free状态,最后1个处于活动状态,因此,我们可以推断首日志和尾日志的位置都在这个VLF上。这个时候执行文件收缩将看不到文件减小的效果。
  3、如何解决这个问题
  那么碰到这种情况,该怎么去收缩日志呢:尽可能多的执行一些能够产生大量日志的操作,这些日志将导致数据库重新利用startoffset靠前的非活动状态的VLF,将首日志的位置定位到这个startoffset,然后做一次事务日志备份,将尾日志也迁移到startoffset靠前的非活动状态的VLF中,如下图,最后再执行DBCC SHRINKFILE即可收缩日志文件。
DSC0007.png

  (图八)日志截断示意图三
  三、重要说明
  前文中一直在说通过日志备份即可解决日志截断的问题,其实这只是最简单的场景。在实际环境中可能有很多因素会影响日志的截断,如:
  活动的事物日志
  日志备份只能截断非活动的日志,如果一个事物长时间运行,此时备份事物日志将不会引起截断发生。
  事物日志分发
  事物日志分发中,只有当日志读取器代理已经读取完待分发的日志后,日志才能变得非活动状态。
  数据库镜像和AlwaysOn
  这两种数据库技术都需要将日志传递到接受端,在传递还没有完成时,日志会一直保留,即使是备份日志也无法截断。
  四、Always on 环境下实践
  先对数据库进行完整备份:
  EXEC sp_configure 'show advanced options', 1;
  RECONFIGURE;
  EXEC sp_configure 'xp_cmdshell', 1;
  RECONFIGURE;
  DECLARE @DbName NVARCHAR(1000);
  DECLARE myCursor CURSOR LOCAL STATIC
  FOR
  SELECT  [name]
  FROM    sysdatabases
  WHERE   [name] NOT IN ( 'master', 'model', 'msdb', 'tempdb' )
  AND name NOT LIKE '%test%'
  AND name NOT LIKE '%bak%'
  AND name NOT LIKE '%demo%'
  AND version IS NOT NULL
  ANDversion <> 0
  ORDER BY [name];
  OPEN myCursor;
  FETCH NEXT FROM myCursor INTO @DbName;
  WHILE ( @@FETCH_STATUS = 0 )
  BEGIN
  DECLARE @strDate AS NVARCHAR(20),
  @strDateBeforeSeven AS NVARCHAR(20),
  @strFileName AS NVARCHAR(255),
  @strFileNameBeforeSeven AS NVARCHAR(255),
  @strCommand AS NVARCHAR(255)
  SET@strDate = CONVERT(NVARCHAR(20),GETDATE(),112);
  SET @strDateBeforeSeven = CONVERT(NVARCHAR(20),GETDATE()-3,112);
  SET @strFileName = 'E:\daybak\['+@DbName+']_bakup_'+@strDate;
  SET @strFileNameBeforeSeven = 'E:\daybak\['+@DbName+']_bakup_'+@strDateBeforeSeven;
  EXEC ('BACKUP DATABASE ['+@DbName+'] TO DISK = ''' + @strFileName + '.bak''')
  SET @strCommand = 'DEL ' + @strFileNameBeforeSeven + '.bak'
  EXEC master.dbo.xp_cmdshell @strCommand
  FETCH NEXT FROM myCursor INTO @DbName;
  END;
  CLOSE myCursor;
  DEALLOCATE myCursor;
  然后对数据库进行事务日志备份并收缩:
  EXEC sp_configure 'show advanced options', 1;
  RECONFIGURE;
  EXEC sp_configure 'xp_cmdshell', 1;
  RECONFIGURE;
  DECLARE @DbName NVARCHAR(1000);
  DECLARE myCursor CURSOR LOCAL STATIC
  FOR
  SELECT  [name]
  FROM    sysdatabases
  WHERE   [name] NOT IN ( 'master', 'model', 'msdb', 'tempdb' )
  AND name NOT LIKE '%test%'
  AND name NOT LIKE '%bak%'
  AND name NOT LIKE '%demo%'
  AND version IS NOT NULL
  ANDversion <> 0
  ORDER BY [name];
  OPEN myCursor;
  FETCH NEXT FROM myCursor INTO @DbName;
  WHILE ( @@FETCH_STATUS = 0 )
  BEGIN
  DECLARE @strDate AS NVARCHAR(20),
  @strDateBeforeSeven AS NVARCHAR(20),
  @strFileName AS NVARCHAR(255),
  @strFileNameBeforeSeven AS NVARCHAR(255),
  @strCommand AS NVARCHAR(255)
  SET@strDate = CONVERT(NVARCHAR(20),GETDATE(),112);
  SET @strDateBeforeSeven = CONVERT(NVARCHAR(20),GETDATE()-3,112);
  SET @strFileName = 'E:\Log_daybak\['+@DbName+']_bakup_'+@strDate;
  SET @strFileNameBeforeSeven = 'E:\Log_daybak\['+@DbName+']_bakup_'+@strDateBeforeSeven;
  EXEC ('BACKUP LOG ['+@DbName+'] TO DISK = ''' + @strFileName + '.log'';USE ['+@DbName+'];DBCC SHRINKFILE(2,100);')
  SET @strCommand = 'DEL ' + @strFileNameBeforeSeven + '.log'
  EXEC master.dbo.xp_cmdshell @strCommand
  FETCH NEXT FROM myCursor INTO @DbName;
  END;
  CLOSE myCursor;
  DEALLOCATE myCursor;

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-424014-1-1.html 上篇帖子: SQL Server排名函数与排名开窗函数 下篇帖子: SQL Server聚合函数与聚合开窗函数
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表