SQL SERVER 日志写入原理浅析

时间:2022-08-24 20:13:25

昨天看到网上有一个关于SQL SERVER 课件,便随手下载了下来看看主要讲了些什么内容,于是看到了下面两个PPT页面

SQL SERVER 日志写入原理浅析

SQL SERVER 日志写入原理浅析

由于第一张PPT上的内容不太准确(日志文件中没有“日志页”的概念,只有VLF的概念,可能是我们对“数据页”的概念太深刻了,因此弄了以“日志页”的概念出来,而PPT中说先更新高速缓冲区中的数据页,然后将事务日志写入到“日志页”,很容易让人理解成先更改高速缓冲区,然后将日志写入到磁盘上的“日志页”),再加上我看PPT时比较"囫囵"(只看到前一张PPT,没有往后翻两下看后面一张PPT).因此我觉得PPT的作者在日志的写入顺序上有问题.索性查了一下资料,然后比较深入的思考了日志的写入顺序问题,同时也纠正了一些自己以往的不正确理解.

该文主要包含以下内容:
     1.SQL SERRVER 日志管理器的大致工作内容与原理.
     2.实例探究SQL SERVER 事务日志的产生与写入磁盘磁盘. 
     3.一些其它的相关思考.

第一部分:SQL SERVER 日志管理器的大致工作原理.

日志管理器承担着事务日志的编排与写入工作。它维护着一个或多个被称之为“日志缓存”的连续的专用内存区域。由于SQL SERVER 事务日志必须按照一定的格式写入到日志文件中,因此日志缓存中的功能之一就是用来编排日志的格式。而当一个日志缓存区域被占满的时候,还有一个或多个日志缓存区域可以被用来保存新产生的日志记录。 
    其次,日志管理器维护着两个日志缓存队列,一个flushQueue,另一个是freeQueue。其中flushQueue包含的是等待被刷新到日志文件(物理磁盘)的日志缓存;freeQueue包含的是已经被刷新并且可以被再次使用的日志缓存。 
    而日志的刷新工作主要一个被称之为“日志编写器”的线程来负责,它将依次遍历 flushQueue,一次仅将一个当前的日志缓存中的内容写入到磁盘上。 
    而日志编写器的刷新工作由什么来触发呢?当一个事务被提交时或者日志缓存被占满时,当前的日志缓存就被放入flushQueue,日志编写器就必须开始工作。日志编写器的工作完成后, 日志管理器将会收到一个写入成功的信号,进而激活所有正在等待日志缓存刷新的所有进程,以继续完成工作。

第二部分:实例探究SQL SERVER 事务日志的产生与写入磁盘。

    当一个更新语句被发出并获得相关锁以后,SQL SERVER 将先更改高速缓冲区中的相关数据页,在更改高速缓冲区中的页时,将会产生一条日志记录并放到日志缓存中,当这个更新语句被提交(COMMIT)时,这条存在于当前日志缓存中的日志记录将首先被成功刷新到磁盘上的日志文件中以后,再返回“更新成功”的确认信息到客户端。以上是事务比较“小”的时候日志写入的相关情况。而当事务比较“大”时,尽管事务没有被COMMIT,而日志也会被写入到磁盘上。 
   下面我将以实例来证明以下几种情况: 
    A. 当事务比较“小”时,只有事务被COMMIT时,日志才会被写入到磁盘上的日志文件中。 
    B. 当事务比较“大”时,尽管事务没有被COMMIT,日志也会被写入磁盘上的日志文件中。

实例一:要证明情况A比较麻烦,因为需要在事务被开始但没有被COMMIT时,查看磁盘上的日志文件中是否有相关的日志记录。而SQL SERVER 虽然提供了一个未公开的查看日志记录的命令DBCC LOG(数据库名),但是这个命令却会将存在于日志缓冲区内没有实际写入磁盘的日志记录一并列出来。因此我不得不借助一个大家熟知的第三方工具Log Explorer。

试验步骤: 
    1.建立一个测试数据库northwind,恢复模式为完整。并且对其进行一次完整备份(这样SQL SERVER 才能将日志保存,否则将会被定期截断),运行CHECKPOINT命令,然后再进行一次事务日志备份以截断所有不活动的日志。运行一下命令DBCC LOG(northwind)看看情况。  
        SQL SERVER 日志写入原理浅析 
   在上图的结果中,你将只能看到Operation分别为LOP_BEGIN_CKPT和LOP_END_CKPT的两条日志。这两条日志是刚刚进行“事务日志备份”而产生的两条日志,而其它的日志已经被截断。

2.测试数据库中建立一个表TEST(ORDER_ID INT,ZDESC   VARCHAR(100) ),然后插入一条测试数据。


 CREATE TABLE TEST 
    ( 
       ORDER_ID    INT, 
       ZDESC         VARCHAR(100) 
     )   GO     INSERT INTO TEST(ORDER_ID,ZDESC)  
                  VALUES( 1,’a’) 
  GO  

成功执行后,我们再运行DBCC LOG(northwind)看看日志的情况: 
    SQL SERVER 日志写入原理浅析

3.然后我们运行以下命令:

         BEGIN TRAN 
              UPDATE TEST SET ZDESC=’B’ 
              WHERE ORDER_ID=1

该命令开始了一个事务,将ZDESC列的值更改为‘B’,但是该事务没有被COMMIT.  
      运行DBCC LOG(northwind)查看日志的情况。 
   SQL SERVER 日志写入原理浅析 
   大家注意看第54条日志,从第54条日志开始,就是我们运行上面的UPDATE事务所产生的所有日志,由于该事务并没有被COMMIT,我们必须想办法查看自54号日志开始的所有日志是否已经保存到了磁盘的日志文件中。这时,我们先将SQL SERVER服务改为手动启动,然后强行重新启动电脑,启动电脑后,在SQL SERVER未启动以前,拷贝northwind的日志文件到其它目录(虽然我们可以在电脑启动后,SQL SERVER 服务启动以后再次运行DBCC LOG(northwind)来查看日志的情况,但是我担心SQLSERRVER在启动的时候会进行恢复工作而对没有提交的日志进行什么处理),我们还是利用Log Explorer来查看拷贝出来的日志文件中的日志记录。 
   我们还是先DBCC LOG(northwind)看看情况: 
   SQL SERVER 日志写入原理浅析 
  大家可以看到,先前的序号64,65号日志已经看不到了,而这两条日志,就是UPDATE语句产生的真正的日志,而54-63是进行修改工作系统内部的一些事务。然后我们再用Log Explorer来看看之前拷贝出来的日志文件中的日志。 
SQL SERVER 日志写入原理浅析

上图是Log Explorer显示的备份出来northwind的日志文件的详细情况,我们可以在上面的表格栏选中某一条日志,然后注意红色框框出来的LSN.LSN:36:87:1这条日志即是我们用DBCC LOG(northiwind)命令所显示的排序为64的即CurrentLSN为:00000036:00000087:0001这条日志,很明显,Log Explorer显示的这条日志和先前DBCC LOG(northiwind)显示出的日志并不相同,因此我们可以断定,一个“小”的事务在未被COMMIT以前,日志已经产生,并且存储在日志缓冲区,但没有写入到磁盘的日志文件中。而一旦该事物被COMMIT,日志将一定会被写入磁盘,这种情况各位园友可以自己去实际验证。

实例二:证明情况B比较简单,我们只需要让SQL SERVER运行一个较“大”的事务,然后观察磁盘上日志文件有没有被自动增长,如果增长了,那么日志肯定被写入到磁盘上了。

实验步骤: 
1.观察northiwind当前日志文件的大小。因为我的northwind是刚刚新建的数据库,日志文件的物理大小为1M. 
2.运行以下脚本,然后在观察日志文件的物理大小。


   BEGIN TRAN 
       DECLARE @I  INT 
       SET @I=1 
       WHILE(@I<99999) 
            BEGIN 
                 UPDATE TEST SET ZDESC=LEFT(NEWID(),10) 
                 SET @I=@I+1 
            END 

该脚本被封装成一个事务,并且没有被提交(COMMIT),运行完成后,我观察到的日志文件的物理大小为38.3M,如下图: 
SQL SERVER 日志写入原理浅析 
很明显,尽管该事务没有被提交(COMMIT),但是,只要日志缓冲区被填满,日志缓存中的日志就会被写入到物理磁盘上。及时我们回滚了该事务,我们依然可以用DBCC LOG(northwind)看到这些被回滚的日志。

第三部分:其它一些相关问题的思考 
    1.当SQL SERVER修改高速缓冲区中的数据页时,日志便会产生,并放入到日志缓存。那么日志究竟是由缓冲区管理器产生后交给日志管理器,还是由日志管理器探测到缓冲区的修改,然后自己产生日志。 
    2.日志缓冲中的日志,究竟要“编排”成什么格式?是否就是我们通过DBCC LOG(northwind)所看到的格式。 
    3.日志编写器在讲日志写入磁盘时,如何知道该写到日志文件的哪一个VLF,也就是说它是如何知晓某个VLF是逻辑上的最后一个VLF。

后文

关于Virtual Log Files(虚拟日志文件)参考这里

关于Write-Ahead Logging (预写日志)参考这里

转自:https://www.cnblogs.com/zhouqiang52154/archive/2009/06/20/1507488.html

SQL SERVER 日志写入原理浅析的更多相关文章

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

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

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

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

  3. sql server 日志文件结构及误操作数据找回

    一. 概述 在sql server 里有数据文件.mdf和日志文件.ldf,日志文件是sqlserver数据库的另一个重要组成部分,日志文件记录了所有事务以及每个事务对数据库所做的修改.为了提高数据库 ...

  4. SQL Server on Linux 理由浅析

    SQL Server on Linux 理由浅析 今天的爆炸性新闻<SQL Server on Linux>基本上在各大科技媒体上刷屏了 大家看到这个新闻都觉得非常震精,而美股,今天微软开 ...

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

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

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

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

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

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

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

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

  9. 误删SQL Server日志文件后怎样附加数据库

    SQL Server日志文件因为误操作被删除,当附加数据库的时候提示:附加数据库失败. 解决办法如下: 1.新建一个同名数据库. 2.停止数据库服务,覆盖新建的数据库主文件(小技巧:最好放在同一个磁盘 ...

随机推荐

  1. The type String cannot be constructed&period; You must configure the container to supply this value&period;

    利用 Enterprise Library 5.0 Microsoft.Practices.EnterpriseLibrary.Common Microsoft.Practices.Enterpris ...

  2. 关于Failed to install helloworld&period;apk on device &&num;39&semi;emulator-5554&excl;的一个解决办法

    好不容易架好了android环境,把该安得都安好了,结果发现在安装过程中创建一个虚拟设备映象就占用了c盘34g的空间,我的系统盘差点瘫了,看来以后就只能先用这一个虚拟设备调试了, 接着说上边这个问题, ...

  3. 设计模式C&num;实现&lpar;七&rpar;——生成器模式

    生成器模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. UML类图: 构成: 1.Builder(接口/抽象类)定义了创建一个产品Product的各个部件的方法,返回创 ...

  4. 十五、struts2中的拦截器(框架功能核心)

    十五.struts2中的拦截器(框架功能核心) 1.过滤器VS拦截器 功能是一回事. 过滤器是Servlet规范中的技术,可以对请求和响应进行过滤. 拦截器是Struts2框架中的技术,实现AOP(面 ...

  5. php编译参数注解--不明白许多参数的作用 慎用 –with-curlwrappers参数

    在Linux下安装PHP,源代码方式安装,总需要配置很多参数.这里列出常用配置参数,并详细用中文解释说明了.给大家一些参考 编译PHP的时候慎用 –with-curlwrappers参数 ./conf ...

  6. javascript高级编程笔记02&lpar;基本概念&rpar;

    ParseInt()函数: 由于Number函数在转换字符串时比较复杂而且不合理,我们常常转换字符串都用parseInt函数, Parseint函数规则: 忽略字符串前面的空格,直到找到第一个非空格字 ...

  7. day8 笔记

    文件操作的最简单步骤open():打开文件,将句柄赋值给一个变量 read()write()等:操作文件 close():关闭文件,一定要关闭文件 ps:python会帮助你保存数据关闭文件,但是要在 ...

  8. 两种ps切图方法(图层&sol;切片)

    两种Ps切图方法 一.      基础操作: a)    Ctrl++ 放大图片,ctrl - -缩小图片 b)    按住空格键space+,点击鼠标左键,拖动图片. c)    修改单位,点击编辑 ...

  9. maven配置本地仓库通用

    只要在settings.xml文件中指定仓库就可以了,然后复制仓库到任何地方都可以使用,eclipse中指定一个settings.xml就可以了 仓库的位置是.locks所在目录

  10. &lt&semi;kafka&gt&semi;&lt&semi;应用场景&gt&semi;&lt&semi;Kafka VS Flume&gt&semi;

    前言 最近在搭一个离线Hadoop + 实时SparkStreaming的日志处理系统,然后发现基本上网上的这种系统都集成了kafka. 自己对kafka有一点点的认识,之前看过官网文档,用过一次,就 ...