为何事务日志自动增加会下降你的性能

在这篇文章里,我想详细谈下为何你要避免事务日志(Transaction Log)上的自动增加操做(Auto Growth operations)。不少运行的数据库服务器,对于事务日志,用的都是默认的日志文件大小和自动增加设置。人们有时会很依赖自动增加机制,由于它们恰好能正常工做。固然,若是它正常工做的话,你没必要太关注它,但很快你会发现会有问题出现。web

只依赖于事务日志的自动增加机制总不是个好主意。首先它会致使严重的日志碎片(Log Fragmentation),在SQL Server启动期间,在你数据库上执行崩溃恢复(Crash Recovery)时会有很大的负面影响。另外,在你数据库里写入事务须要等待,只要事务日志触发了自动增加机制。sql

当事务日志的自动增加机制发生时,SQL Server总要零初始化新块,这个会在文件末尾加上。这和你的SQL Server实例是否用即时文件初始化(Instant File Initialization)特权——事务日志总会零初始化。这上面的缘由很是明显:当SQL Server在过去已经完成事务日志的环绕式处理(wrap-around ),崩溃恢复(Crash Recovery)须要知道在哪里停。数据库

零初始化的问题是会占用更多的时间(取决与你的自动增加率,还有你的存储速度)。在此期间没有别的事务能够写事务日志记录到事务日志。在事务日志管理器上会有闩锁形成的阻塞。所以你的写入事务会进入挂起状态(直到它们得到须要的闩锁),它们就等啊,等啊,等啊,直到你的事务日志自动增加完成。让咱们用一个简单的例子演示下。windows

首先我为这个演示建立一个新的数据库。对于这个数据库,这里我不用默认的设置,对于事务日志,我指定了10GB的自动增加系数。这个的确是个很差的作法,但我只是用它来展现这个设置的反作用。请不要在你的生产数据库里使用这个错误配置!!! 服务器

 1 -- Create a new database with 10 GB Auto Growth for the Transaction Log
 2 CREATE DATABASE AutoGrowthTransactionLog ON PRIMARY 
 3 (
 4     NAME = N'AutoGrowthTransactionLog', 
 5     FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\AutoGrowthTransactionLog.mdf',
 6     SIZE = 5120KB, 
 7     FILEGROWTH = 1024KB
 8 )
 9 LOG ON 
10 (
11     NAME = N'AutoGrowthTransactionLog_log',
12     FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\AutoGrowthTransactionLog_log.ldf',
13     SIZE = 1024KB,
14     FILEGROWTH = 10240000KB -- 10 GB Auto Growth!
15 )
16 GO

 下一步里我在数据库里建立2个表。第1个表我经过插入一些日志来快速填充个人事务日志。在事务日志自动增加阶段,咱们在第2个表里插入新的记录来证实这个事务会被自动增加机制阻塞。session

 1 -- Create a new table, every records needs a page of 8kb
 2 CREATE TABLE Chunk
 3 (
 4     Col1 INT IDENTITY PRIMARY KEY,
 5     Col2 CHAR(8000)
 6 )
 7 GO
 8 
 9 -- Another simple table
10 CREATE TABLE Foo
11 (    
12     Bar INT NOT NULL
13 )
14 GO

如今咱们已经建立了必须的数据库对象,因次我能够经过新的没有当即提交的事务来填充事务日志:函数

1 -- Begin a new transaction, that blocks the 1st VLF in the Transaction Log
2 BEGIN TRANSACTION
3 INSERT INTO Chunk VALUES (REPLICATE('x', 8000))
4 GO

由于咱们如今有了进行中,没提交的事务,SQL Server不能重用那部分事务日志,即这个事务存储的事务日志。它们有须要回滚的可能。所以如今我经过不一样的会话插入66条其余记录来填充事务日志:性能

1 INSERT INTO AutoGrowthTransactionLog.dbo.Chunk VALUES (REPLICATE('x', 8000))
2 GO 66

最后在第一个会话里提交咱们的事务:测试

1 COMMIT

这意味着在咱们面前有一个几乎满的的事务日志,咱们能够经过DBCC LOGINFO来验证:spa

1 DBCC LOGINFO

如今当咱们往表里插入兮的记录时,事务日志已经没有可用空间了,SQL Server进入事务日志的自动增加。

1 -- This statement will trigger the Auto Growth mechanism!
2 INSERT INTO Chunk VALUES (REPLICATE('x', 8000))
3 GO

在自动增加期间的同时,为了监控发生了什么,咱们能够在SSMS里打开新的一个会话窗口,尝试在第2个表插入另外的记录——表Foo

1 -- This statement is now blocked by the Auto Growth mechanism.
2 INSERT INTO Foo VALUES (1)
3 GO

这个SQL 语句会阻塞,由于事务要写入事务日志记录的事务日志,当前不可用。为了进一步分析这个阻塞情形,你能够打开第3个会话窗口,执行下列2个SQL语句:

1 -- Analyze the blocking situation
2 SELECT wait_type, * FROM sys.dm_exec_requests
3 WHERE session_id IN (54, 55)
4 
5 SELECT wait_type, * FROM sys.dm_os_waiting_tasks
6 WHERE session_id IN (54, 55)
7 GO

(额,俺本机测试失败………………)

从代码里能够看到,我用2个DMV sys.dm_exec_requests 和 sys.dm_os_waiting_tasks对2个会话都进行了跟踪——触发自动增加的会话,和被自动增加机制阻塞的会话。在这里,触发自动增加的会话里有所谓的抢占等待类型(Preemptive Wait Type)——PREEMPTIVE_OS_WRITEFILEGATHER。抢占等待类型是由SQL Server返回的等待类型,当SQL Server 执行一个WIN32 API函数在调度机制以外时。这里自动增加是经过WriteFileGather的WIN32 API函数完成的。

INSERT语句尝试在Foo表里插入新的记录出现LATCH_EX等待类型。如你从DMV sys.dm_os_waiting_tasks 里的resource_description列所见,在SQL Server的日志管理器上须要得到闩锁。你能够经过查询DMV sys.dm_os_latch_stats 限制lactch class为LOG_MANAGER再次确认。在那个特定闩锁上你会看到一些等待。那个闩锁是事务获取的,由事务日志的自动增加触发,只要这个闩锁要得到,每一个其余写事务都会被阻塞。所以在系统上有大量等待时间时,这暗示这在事务日志里当前有自动增加问题须要处理。

但愿我已经用这个日志说服你,依赖于事务日志的自动增加机制并非最好的解决方案。用这个简单的例子能够看到,在你数据库里每一个被自动增加操做阻塞的写入事务会发生阻塞,这确定会伤及你数据库的吞吐量和扩展性。为了保证你有很好的事务日志性能,你能够最佳想实践下这个文章

感谢关注! 

参考文章:

https://www.sqlpassion.at/archive/2014/01/07/why-transaction-log-auto-growths-are-degrading-your-performance/

相关文章
相关标签/搜索