码迷,mamicode.com
首页 > 其他好文 > 详细

Log Reservation

时间:2017-02-05 22:50:23      阅读:279      评论:0      收藏:0      [点我收藏+]

标签:.sql   其他   created   run   handle   sqlserver   targe   files   keep   

本文是在阅读《SQL Server Transaction Log Management》时发现以往对Log Grows的理解比较片面,大部分内容引自原文,记录此文仅用于加深理解。
在讨论日志截断和空间重用时,我们会看到类似下面的结构图
技术分享
A transaction log with 8 VLFs, after truncation
由于checkpoint(或者日志备份),VLF1和VLF2被截断并处于非活动状态。现在逻辑日志的开始位置是VLF3。VLF8因为还未被使用,所以仍然处于非活动状态。
当活动日志到达VLF7的尾部时会发生什么?
最简单的情形,一旦日志的逻辑结尾到达一个VLF的尾部,SQLServer将会开始重用下一个非活动的VLF。在上图中,将会是VLF8。当VLF8被写满之后,将会环绕到VLF1和VLF2并且重用它们。
如果没有可用的VLF可以被重用,那么日志需要自动增长,并分配更多的VLF。如果由于自动增长被禁用或者物理磁盘空间被占满,那么活动日志的逻辑尾部将会是日志文件的物理尾部。此时事务日志就会被写满,触发9002错误。
How SQL Server Grows the LOG
一直以为SQLServer先重用非活动的VLF,然后再启用自动增长。实际并非如此!!!
首先创建测试数据库及数据表

技术分享
/********** SQL Server Transaction Log Management->Chapter2 **********/
--Listing 2.1:  Re-creating the TestDB database, plus PrimaryTable_Large
USE master ; 
 
IF DB_ID(TestDB) IS NOT NULL  
    DROP DATABASE TestDB ; 
 
CREATE DATABASE TestDB ON PRIMARY  
 (   NAME = NTestDB 
   , FILENAME = ND:\SQLData\TestDB.mdf 
   , SIZE = 199680KB 
   , FILEGROWTH = 16384KB 
 ) 
 LOG ON 
 (   NAME = NTestDB_log 
   , FILENAME = ND:\SQLData\TestDB_log.ldf 
   , SIZE = 2048KB 
   , FILEGROWTH = 16384KB 
 ); 
GO 
 
USE TestDB 
Go 
IF OBJECT_ID(dbo.PrimaryTable_Large, U) IS NOT NULL  
    DROP TABLE dbo.PrimaryTable_Large 
GO 
CREATE TABLE PrimaryTable_Large 
    ( 
      ID INT IDENTITY 
             PRIMARY KEY , 
      SomeColumn CHAR(4) NULL , 
      Filler CHAR(1500) DEFAULT ‘‘ 
    ); 
GO 
 
ALTER DATABASE TestDB SET RECOVERY FULL;

/*Initial full database backup of TestDB*/ 
BACKUP DATABASE TestDB 
TO DISK =D:\SQLBackups\TestDB.bak 
WITH INIT; 
GO 
 
DBCC SQLPERF(LOGSPACE) ; 
/*Log Size (MB): 2, Log Space used (%): 18*/

--检查恢复模式
SELECT NAME,recovery_model_desc FROM sys.databases WHERE NAME=TestDB
--last_log_backup_lsn为空则处于自动截断模式
SELECT DB_NAME(database_id) AS DatabaseName,last_log_backup_lsn
FROM master.sys.database_recovery_status WHERE database_id=DB_ID(TestDB)
View Code

查看数据库中VLFs信息

技术分享
--Listing 2.2:  Four VLFs in the newly created TestDB database.
-- how many VLFs? 
USE TestDB; 
GO 
DBCC Loginfo 
GO
View Code

技术分享
结果返回4行意味着有4个VLFs。
FileSize列(单位Bytes)显示前3个VLFs小于0.5MB,第4个大于0.5MB,总大小为日志文件初始大小(2MB)。
Status=2表示活动的VLF,它不能被重写;Status=0表示非活动的VLF,它的空间可以重用。当前我们有一个活动的VLF。
FSeqNo列表示VLFs在日志文件中的逻辑顺序。
CreateLSN列是自动增长或者ALTER DATABASE操作的LSN,它们会增长日志并创建VLF。0值表示此VLFs是创建数据库时所创建的。
现在让我们往表中插入1500行记录,然后重新检查统计值

技术分享
--Listing 2.3:  VLFs after adding 1,500 rows.
USE TestDB; 
GO 
INSERT  INTO PrimaryTable_Large 
        ( SomeColumn, Filler 
        ) 
        SELECT TOP 1500 
                abcd , 
                REPLICATE(a, 200)  
        FROM    msdb.sys.columns a 
                CROSS JOIN msdb.sys.columns b 
GO 
 
DBCC SQLPERF(LOGSPACE) ;
/*Log Size: 18 MB ; Log Space Used: 16%*/ 
 
DBCC Loginfo 
GO
View Code

技术分享
在插入1500行后,触发了日志文件自动增长。我们可以看到4个新的VLFs。增长量是16MB,每一个VLF是4MB。新VLFs的第一个(FSeqNo 43)是活动的,其他的未使用,
当前日志文件是18MB,使用率为16%。
现在执行一个日志备份,并重新检查统计值

技术分享
--Listing 2.4:  VLF usage after log backup.
/*now a log backup*/ 
BACKUP Log TestDB 
TO DISK =D:\SQLBackups\TestDB_log.bak 
WITH INIT; 
GO 
 
DBCC SQLPERF(LOGSPACE) ; 
/*Log Size: 18 MB ; Log Space Used: 6%*/ 
 
-- how many VLFs?  
USE TestDB; 
GO 
DBCC Loginfo 
GO
View Code

技术分享
日志备份后,空间使用率降到6%,并且最原始的4个VLFs被截断(可重用)。让我们再次增长日志,增加15000条记录

技术分享
BEGIN TRAN
INSERT  INTO PrimaryTable_Large 
        ( SomeColumn, Filler 
        ) 
        SELECT TOP 15000 
                abcd , 
                REPLICATE(a, 200)  
        FROM    msdb.sys.columns a 
                CROSS JOIN msdb.sys.columns b
--COMMIT TRAN 
View Code

注意这里我开启了一个事务,暂时不提交。VLFs信息
技术分享
SQL Server填充了VLFs 43-46(第一次自动增长创建)。最原始的4个VLFs(39-42,创建数据库时所创建)可以被重用,但是SQL Server选择了自动增长,而不是重用前面4个VLFs,然后填充新VLFs中的前3个(47-49)。
在本例中,SQL Server选择自动增长,是因为如果使用最原始的4个VLFs,将没有足够的log reservation空间用于回滚事务。回滚事务需要一系列的操作来undo原事务的影响。这些compensating operations会产生日志记录,类似于其他数据库变更。
Log reservation is the amount of log space(per transaction) that SQL Server must keep free in order to accommodate these compensation log records.
因此,大的事务写入很多行将需要非常大的log reservation,在事务提交后SQL Server会立刻释放这些额外的空间。
在另一个会话查看事务的log reservation

技术分享
USE TestDB 
GO 
SELECT DTST.[session_id],
       --DES.[login_name] AS [Login Name],
       DB_NAME(DTDT.database_id)  AS [Database],
       DTDT.[database_transaction_begin_time] AS [Begin Time],
       DATEDIFF(ms, DTDT.[database_transaction_begin_time], GETDATE()) AS [Duration ms],
       CASE DTAT.transaction_type
            WHEN 1 THEN Read/Write
            WHEN 2 THEN Read-Only
            WHEN 3 THEN System
            WHEN 4 THEN Distributed
       END                        AS [Transaction Type],
       CASE DTAT.transaction_state
            WHEN 0 THEN Not fully initialized
            WHEN 1 THEN Initialized, not started
            WHEN 2 THEN Active
            WHEN 3 THEN Ended
            WHEN 4 THEN Commit initiated
            WHEN 5 THEN Prepared, awaiting resolution
            WHEN 6 THEN Committed
            WHEN 7 THEN Rolling back
            WHEN 8 THEN Rolled back
       END                        AS [Transaction State],
       DTDT.[database_transaction_log_record_count] AS [Log Records],
       DTDT.[database_transaction_log_bytes_used] AS [Log Bytes Used],
       DTDT.[database_transaction_log_bytes_reserved] AS [Log Bytes Reserved],
       DEST.[text] AS [Last Transaction Text],
       DEQP.[query_plan] AS [Last Query Plan]
FROM   sys.dm_tran_database_transactions DTDT
       INNER JOIN sys.dm_tran_session_transactions DTST
            ON  DTST.[transaction_id] = DTDT.[transaction_id]
       INNER JOIN sys.[dm_tran_active_transactions] DTAT
            ON  DTST.[transaction_id] = DTAT.[transaction_id]
       INNER JOIN sys.[dm_exec_sessions] DES
            ON  DES.[session_id] = DTST.[session_id]
       INNER JOIN sys.dm_exec_connections DEC
            ON  DEC.[session_id] = DTST.[session_id]
       LEFT JOIN sys.dm_exec_requests DER
            ON  DER.[session_id] = DTST.[session_id]
       CROSS APPLY sys.dm_exec_sql_text (DEC.[most_recent_sql_handle]) AS DEST 
       OUTER APPLY sys.dm_exec_query_plan (DER.[plan_handle]) AS DEQP
WHERE  DB_NAME(DTDT.database_id) = TestDB
ORDER BY
       DTDT.[database_transaction_log_bytes_used] DESC; 
--ORDER BY [Duration ms] DESC; 

--简单点使用这个
SELECT  d.name ,
        --session_id ,
        d.recovery_model_desc ,
        --database_transaction_begin_time ,
        database_transaction_log_record_count ,
        database_transaction_log_bytes_used ,
        database_transaction_log_bytes_reserved ,
        DATEDIFF(ms, database_transaction_begin_time, GETDATE())
                AS [Duration ms]
FROM    sys.dm_tran_database_transactions AS dt
        INNER JOIN sys.dm_tran_session_transactions AS st
               ON dt.transaction_id = st.transaction_id
        INNER JOIN sys.databases AS d ON dt.database_id = d.database_id
WHERE d.name = TestDB
View Code

技术分享
我们可以看到SQL Server Reserved约为5.3MB,而原始的4个VLFs总大小为2MB,不足以存储必要的compensation operations。因此SQL Server优先选择的自增长。
下面内容引自《SQL Server Transaction Log Management》->Chapter 8: Optimizing Log Throughput->Page 178

The logging subsystem reserves space when logging a transaction to ensure that the log can‘t run out of space during a rollback. As such, the required log space is actually greater than the total size of log records for the operation.
In short, a rollback operation logs compensation log records, and if a rollback were to run out of log space, SQL Server would have to be mark the database as suspect. This log reservation is not actually "used" log space, it‘s just a specific amount of space that must remain free, but it can trigger auto-growth events if the log fills to a point where the "used space + reserved space = log size," and it is counted as used space for the purposes of DBCC SQLPERF(LOGSPACE).

因此,在事务提交后你可能会发现DBCC SQLPERF(LOGSPACE)返回空间使用效率降低,即使数据库处于完整模式并且没有进行日志备份
技术分享
在事务提交前/*Log Size: 34 MB ; Log Space Used: 96%*/
在事务提交后/*Log Size: 34 MB ; Log Space Used: 79%*/
中间的差值为34*(96-79)/100.0=5.78 MB,与事务Reserved的大小相当。
In the absence of any log reservation calculation, SQL Server would simply have filled the available VLFs and then grown the log.
因此,事务日志的增长模式很大程度上取决于用户的活动模式和事务的大小。如果我们将原来一次插入15000行拆分成三次小的插入(比如1500行、5000行、3500行),我们将会看到类似下面的VLFs信息
技术分享
注意SQL Server填充了VLFs 43-46(第一次自动增长创建),然后环绕到原始VLFs的前两个(现在重用47和48)。这是因为原始的VLFs能够容纳小事务插入所需的log reservation。然后SQL Server再次自动增长,并且使用新的VLFs(49)而不是原始剩余的VLFs。
上面的例子只是插入相对较少的行,大家可以测试插入百万甚至更多的行,然后查看日志使用以及VLFs。当然这又涉及log fragmentation问题,大家可以查阅相关的文章。

Log Reservation

标签:.sql   其他   created   run   handle   sqlserver   targe   files   keep   

原文地址:http://www.cnblogs.com/Uest/p/6368262.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!