2016-07-26 73 views
2

我想了解为什么下面的代码写入日志文件。我是一名初学者,并且已经读过,当数据库处于简单恢复模式时,不会写入日志。但下面的代码是写在全部和简单恢复模式。在这种情况下,日志文件是用简单的恢复模式写入的?日志文件正在增长与简单恢复模式

代码:

Declare @val int =1 
set nocount on 

BEGIN TRAN 
while @val <= 100000 
begin 
    insert into LoadTable values (REPLICATE('P',1000)) 
    set @val = @val + 1 
end 
ROLLBACK TRAN 

回答

2

首先,您的理解是,当数据库处于简单恢复模式时,没有任何内容写入日志文件,是错误

SQL Server在所有恢复模式下写入日志文件,唯一的区别是在简单恢复模式下它会自动回收日志空间(当它可以)并且还记录维护事务的最小内容(如果必须回滚一个)。

尽管在完全恢复模式下,我们必须执行事务日志备份以使SQL Server的空间可用于进一步日志记录。

现在回到你的例子:

Declare @val int =1 
set nocount on 

BEGIN TRAN    --<-- Your Transaction starts here 
while @val <= 100000 
begin 
    insert into LoadTable values (REPLICATE('P',1000)) 
    set @val = @val + 1 
end 
ROLLBACK TRAN    --<-- Your Transaction ends here 

在你的榜样,后交易已经开始,它结束之前(回滚/提交)有很多活动正在进行时,SQL Server需要登录如果您决定像以前那样回滚事务,那么此活动就是如此,因此越来越多的日志将被写入日志文件直到事务完成(Committed或Rollback)。

在这个特定的例子中,sql server必须保存100000个插入语句的日志,以防万一出现错误。


另一种略有不同的版本,您的查询可能是......

Declare @val int =1 
set nocount on 


while @val <= 100000 
begin 

    BEGIN TRAN  --<-- Your Transaction starts here 

     insert into LoadTable values (REPLICATE('P',1000)) 

    ROLLBACK TRAN  --<-- Your Transaction Ends here   

    CHECKPOINT; 

     set @val = @val + 1 
end 
在此略有不同的版本相同的T-SQL命令的

现在有少了很多活动交易之后怎么回事已经开始,并且在它完成之前,因此sql服务器必须记录非常少的数据,并且如果有的话,事务文件将增长得非常少。

在此示例中,sql服务器必须一次只记录一个insert语句,因为它在该点之后被提交或回滚。

+0

正如@Bee_Riii所提到的,一旦事务被提交,简单恢复将不会保留日志。怎么运行的?在我的例子中,如果我用COMMIT替换ROLLBACK,日志仍然存在。 – p2k

+1

是什么让你认为日志仍然存在?由于日志文件的大小?如果文件的大小已经增大并不意味着它有内部日志,这意味着文件不够大,无法写入sql server必须写入的所有日志,因此文件已经增长。磁盘上的日志文件大小和该文件中的日志是两回事。 –

+0

明白了,当我试图收缩日志文件时,它说93%的可用空间可用。这意味着它不是日志数据而是它的文件大小。这清除了我的怀疑。谢谢 – p2k

0

退房更详细的恢复模式here.

DML查询将永远写入日志,以便能够回滚。在基本术语中,一旦事务被提交,简单恢复将不会保留日志,但它们在执行时仍会写入。

2

我把所有的信息从这里临SQL服务器内部2014
https://www.amazon.com/Pro-Server-Internals-Dmitri-Korotkevitch/dp/1430259620

TL; DR;

恢复模式SIMPLE和FULL不同于SQL Server如何使虚拟日志文件(VLF)失活。
总结:
1 - “在SIMPLE恢复模型中,事务日志的活动部分以VLF开头,VLF包含最早的活动事务的最旧LSN或最后一个CHECKPOINT”;
2“ - 在完整或是批量日志恢复模型,事务日志的活动部分与VLF开始,其中包含最早的以下内容:
LSN最后一个日志备份的
LSN最早的活动事务
的读取事务日志记录”

LSN =日志序列号=独特,自动递增ID

更详细的解释的过程中 LSN

假设这是SQL Server内存模型:
1 - 缓冲池是SQL Server存储索引,行等内存的地方;
2 - 日志缓冲区是事务日志的一小部分(每个数据库64KB)缓冲区;
3 - 数据文件是SQL Server将存储索引,行等...在磁盘中的地方;
4 - 事务日志是......磁盘中的事务日志。

假设我们有一个数据库处于以下状态。

/--------------- IN MEMORY --------------\/------------ IN DISK -----------\ 
|--------------------------------------------------------------------------| 
|Buffer Pool    | Log Buffer | Data File  |Transaction Log | 
|---------------------------|-------------|---------------|----------------| 
|Page 1:24312    |    |Page: 1:24312 |LSN:7213  | 
|IsDirty: False    |    |LSN: 4845  |    | 
|LSN: 4845     |    |Page: 1:24313 |    | 
|...      |    |LSN: 2078  |    | 
|Page 1:26912    |    |...   |    | 
|isDirty:False    |    |Page: 1:26911 |    | 
|LSN:1053     |    |LSN: 2078  |    | 
|       |    |Page: 1:26912 |    | 
|       |    |LSN: 2078  |    | 
|---------------------------|-------------|---------------|----------------| 

现在假设进行了一个更改,一个简单的更新。
第一步是将日志记录插入日志缓冲区。

/--------------- IN MEMORY --------------\/------------ IN DISK -----------\ 
|--------------------------------------------------------------------------| 
|Buffer Pool    | Log Buffer | Data File  |Transaction Log | 
|---------------------------|-------------|---------------|----------------| 
|Page 1:24312    |LSN:7214  |Page: 1:24312 |LSN:7213  | 
|IsDirty: False    |Op:Update |LSN: 4845  |    | 
|LSN: 4845     |Page:1:24312 |Page: 1:24313 |    | 
|...      |OldLsn:4845 |LSN: 2078  |    | 
|Page 1:26912    |Row:2  |...   |    | 
|isDirty:False    |Tran:T1  |Page: 1:26911 |    | 
|LSN:1053     |PrevLSN:7141 |LSN: 2078  |    | 
|       |    |Page: 1:26912 |    | 
|       |    |LSN: 2078  |    | 
|---------------------------|-------------|---------------|----------------| 

,然后更改内存中的数据页(我只改变IsDirty简化)

/--------------- IN MEMORY --------------\/------------ IN DISK -----------\ 
|--------------------------------------------------------------------------| 
|Buffer Pool    | Log Buffer | Data File  |Transaction Log | 
|---------------------------|-------------|---------------|----------------| 
|Page 1:24312    |LSN:7214  |Page: 1:24312 |LSN:7213  | 
|IsDirty: TRUE    |Op:Update |LSN: 4845  |    | 
|LSN: 4845     |Page:1:24312 |Page: 1:24313 |    | 
|...      |OldLsn:4845 |LSN: 2078  |    | 
|Page 1:26912    |Row:2  |...   |    | 
|isDirty:False    |Tran:T1  |Page: 1:26911 |    | 
|LSN:1053     |PrevLSN:7141 |LSN: 2078  |    | 
|       |    |Page: 1:26912 |    | 
|       |    |LSN: 2078  |    | 
|---------------------------|-------------|---------------|----------------| 

这一直持续到日志缓冲区已满,或事务被提交。 提交在日志缓冲区中生成另一个条目,其中OP是提交并将整个缓冲区刷新到磁盘。

/--------------- IN MEMORY --------------\/------------ IN DISK -----------\ 
|--------------------------------------------------------------------------| 
|Buffer Pool    | Log Buffer | Data File  |Transaction Log | 
|---------------------------|-------------|---------------|----------------| 
|Page 1:24312    |    |Page: 1:24312 |LSN:7213  | 
|IsDirty: TRUE    |    |LSN: 4845  |    | 
|LSN: 4845     |    |Page: 1:24313 |LSN:7214  | 
|...      |    |LSN: 2078  |<ALL PROPERTIES>| 
|Page 1:26912    |    |...   |    | 
|isDirty:False    |    |Page: 1:26911 |LSN:7215  | 
|LSN:1053     |    |LSN: 2078  |Op:Commit  | 
|       |    |Page: 1:26912 |    | 
|       |    |LSN: 2078  |LSN:7216  | 
|       |    |    |Op:Checkpoint | 
|---------------------------|-------------|---------------|----------------| 

此时SQL Server将回答事务成功的客户端。 值得指出的是内存中的脏页还没有被发送到磁盘。 在这一点上,如果发生了什么事SQL Server将能够恢复到这个确切点的所有更改。
这种技术被称为预写日志记录和详细信息,请参阅:

重复历史超越ARIES
http://www.vldb.org/conf/1999/P1.pdf

在某些时刻检查点进程将创建刷新缓冲池中的所有脏页的检查点操作到磁盘。如上例所示,检查点操作也出现在事务日志中。

考虑到这一点,我们可以看到SQL Server如何处理事务日志。

虚拟日志文件

的事务日志的磁盘上是细分在虚拟日志文件(VLF)。您可以查看运行:

DBCC LOGINFO 

最重要的部分就是虚拟日志文件(VLF)可以被归类为有效或无效。

SQL Server只在其恢复模型中使用事务日志的活动部分。所以简单和完全之间的区别是当一个VLF变成非活动时。 SQL Server停用VLF是因为事务日志是一个环绕文件,这意味着“当逻辑日志文件的末尾达到物理文件的末尾时,日志将环绕它”。例如:

/------ACTIVE-----\/----------------INACTIVE----------------\/--------ACTIVE---\ 
|------------------------------------------------------------------------------| 
|  |   |   |   |   |   |  |   | 
| VLF1 | VLF2 | VLF3 | VLF4 | VLF5 | VLF6 | VLF7 | VLF8 | 
|  |   |   |   |   |   |  |   | 
|------------------------------------------------------------------------------| 

因此,如果由于某种原因没有VLF变为无效,则事务日志将需要无限增长。

处于简单恢复

回过头来看看这个例子。在检查点之后,并且所有内容都刷新到磁盘,SIMPLE恢复中的SQL Server将只维护激活的VLF:
1 - 包含最早的活动事务的最旧的LSN;或
2 - 最后一个检查点。

例如:

一个检查点

/------INACTIVE---\/----------------ACTIVE-------\/---------INACTIVE-----------\ 
|------------------------------------------------------------------------------| 
|  |   |   |   |   |   |  |   | 
| VLF1 | VLF2 | VLF3 | VLF4 | VLF5 | VLF6 | VLF7 | VLF8 | 
|  |   |   |   |   |   |  |   | 
|------------------------------------------------------------------------------| 
       ^ ^ ^  ^^
        |  |  |   | |> End of logical LOG file 
        |  |  |   |> Current LSN 
        |  |  |> Minumin LSN (Oldest Active Transaction) 
        |  |> Last Checkpoint 
        |> Start of Logical LOG file 

边检站

/------INACTIVE---------------\/----ACTIVE-------\/---------INACTIVE-----------\ 
|------------------------------------------------------------------------------| 
|  |   |   |   |   |   |  |   | 
| VLF1 | VLF2 | VLF3 | VLF4 | VLF5 | VLF6 | VLF7 | VLF8 | 
|  |   |   |   |   |   |  |   | 
|------------------------------------------------------------------------------| 
          ^^  ^^
           | |   | |> End of logical LOG file 
           | |   |> Current LSN (Checkpoint Occurs) 
           | |> Minumin LSN (Oldest Active Transaction) 
           |> Start of Logical LOG file 

SQL服务器已经灭活包含最后一个检查点的VLF3之后之前因为:
1 - 新检查点将内存中的所有脏页面强制到磁盘上。因此,不需要重做存储在VLF3中的任何更改,因为最早的活动事务处于VLF4中;
2 - 但是,正因为如此,我们仍然需要VLF4来支持所有活动事务的回滚。

处于完全恢复

同样的过程发生在完全恢复,但现在最后的VLF将保持活跃将是最古老的来自:
1 - 最后的日志备份的LSN;
2-OLDEST ACTIVE TRANSACTION的LSN;或
3 - 读取事务日志记录的进程的LSN。

例如

/------INACTIVE---------------\/----ACTIVE-------\/---------INACTIVE-----------\ 
|------------------------------------------------------------------------------| 
|  |   |   |   |   |   |  |   | 
| VLF1 | VLF2 | VLF3 | VLF4 | VLF5 | VLF6 | VLF7 | VLF8 | 
|  |   |   |   |   |   |  |   | 
|------------------------------------------------------------------------------| 
          ^^ ^^ ^
           | |  | | |> End of logical LOG file 
           | |  | |> Current LSN (Checkpoint Occurs) 
           | |  |> Minumin LSN (Oldest Active Transaction) 
           | |> Replication log Reader 
           |> Start of Logical LOG file 
在这个例子中,复制日志读取迫使VLF4保持活跃

/------INACTIVE---\/----------------ACTIVE-------\/---------INACTIVE-----------\ 
|------------------------------------------------------------------------------| 
|  |   |   |   |   |   |  |   | 
| VLF1 | VLF2 | VLF3 | VLF4 | VLF5 | VLF6 | VLF7 | VLF8 | 
|  |   |   |   |   |   |  |   | 
|------------------------------------------------------------------------------| 
       ^^  ^ ^^ ^
        | |   |  | | |> End of logical LOG file 
        | |   |  | |> Current LSN (Checkpoint Occurs) 
        | |   |  |> Minumin LSN (Oldest Active Transaction) 
        | |   |> Replication log Reader 
        | |> Last Transaction Log Backup  
        |> Start of logical LOG file 

,并在这个例子中,“最后的事务日志备份”被强迫VLF3保持活跃。

我希望这些有助于更好地理解SQL Server的工作原理。