日志文件正在以简单恢复模式增长

时间:2016-07-26 12:34:35

标签: sql-server sql-server-2012

我试图了解为什么以下代码正在写入日志文件。我是初学者并且已经读过,当数据库处于简单恢复模式时,不会写入日志。但是下面的代码是在FULL和SIMPLE恢复模式下编写的。在哪些情况下,日志文件是用简单的恢复模式写的?

代码:

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

3 个答案:

答案 0 :(得分: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 server必须记录非常少的数据,并且事务文件将变得非常少如果有的话。

在此示例中,sql server必须一次只保留1个insert语句的日志,因为它在该点之后被提交或回滚。

答案 1 :(得分:2)

我从Pro SQL Server Internals 2014获取了所有信息 https://www.amazon.com/Pro-Server-Internals-Dmitri-Korotkevitch/dp/1430259620

<强> TL; DR;

恢复模式SIMPLE和FULL因SQL Server将如何取消激活虚拟日志文件(VLF)而不同。
总结:
1 - &#34;在SIMPLE恢复模型中,事务日志的活动部分以VLF开始,VLF包含最早的活动事务的最旧LSN或最后一个CHECKPOINT&#34 ;;
2 - &#34;在FULL或BULK-LOGGED恢复模型中,事务日志的活动部分以VLF开始,VLF包含以下最旧的: 最后一次日志备份的LSN
LSN最早的活跃交易
读取事务日志记录&#34;

的进程的LSN

LSN =日志序列号=唯一的自动递增ID

更详细的解释

假设这是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      |                |
|---------------------------|-------------|---------------|----------------|

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

/--------------- 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将能够恢复到这个确切点的所有更改 这种技术称为Write Ahead Logging,有关详细信息,请参阅:

重复ARIES之外的历史 http://www.vldb.org/conf/1999/P1.pdf

在某些时刻,Checkpoint进程将创建一个CHECKPOINT操作,将所有Dirty页面从Buffer Pool刷新到磁盘。检查点操作也会出现在事务日志中,如上例所示。

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

虚拟日志文件

磁盘上的事务日志被细分为虚拟日志文件(VLF)。你可以看到这个运行:

DBCC LOGINFO

重要的是,虚拟日志文件(VLF)可以分为活动或非活动。

SQL Server仅在其恢复模型中使用事务日志的活动部分。因此,SIMPLE和FULL之间的区别在于VLF成为非活动状态。 SQL Server取消激活VLF,因为事务日志是一个环绕文件,这意味着,当逻辑日志文件的末尾到达物理文件的末尾时,日志将其包裹起来&#34;。例如:

/------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 Server已停用包含最后一个检查点的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

并且在此示例中,&#34;最后一个事务日志备份&#34;迫使VLF3保持活跃状态​​。

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

答案 2 :(得分:0)

查看有关恢复模式的更多详细信息here.

DML查询将始终写入日志以便能够回滚。在基本术语中,简单恢复不会在事务提交后保留日志,但在执行时仍然会写入。