当一切正常时,没有必要特别留意什么是事务日志,它是如何工做的。你只要确保每一个数据库都有正确的备份。当出现问题时,事务日志的理解对于采起修正操做是重要的,尤为在须要紧急恢复数据库到指定点时。这系列文章会告诉你每一个DBA应该知道的具体细节。sql
这篇文章会列出致使事务日志过分增加的常见的问题和错误管理形式,包括:数据库
固然,若是增加没检查,日志文件会扩展直到吞没全部可用磁盘空间或日志文件的最大大小,在这个时候你会收到该死的9002错误(事务日志已满),数据库会变成只读。这篇文章会谈处处理日志不断增加和9002错误的正确方法,还会解释下一般建议截断或收缩事务日志为何是危险的。安全
最后,咱们会谈下保证你日志文件是平稳和可预见增加的措施,还有日志碎片的最小化问题。在忙碌的数据库里,大型的事务日志会是一个简单的生活事实,若是管理稳当的话,这并非件坏事,即便大部分时间日志文件空间不可用。服务器
任什么时候候日志文件须要增加,额外的空间被分配,这个空间平均分到VLS里,基于被分配空间数。网络
例如,日志文件默认会有2MB的初始大小,10%的自动增加率(来自model数据库的配置)。这就是说,日志文件开始至少会很小的增加,所以会有大量的小VLF。session
当咱们在很大的块上分配额外空间时,例如当一次操做初始16GB的大小,结果事务日志会有很小数量的VLF。架构
过高数量的小VLF,这样的状况称为日志文件碎片,会影响到性能,尤为在故障恢复,还原和备份,特别是日志备份。换句话说,它会影响读取日志文件的操做性能。咱们会在第8篇详细讨论这个问题。并发
事务日志VLF——太多仍是太少?异步
SQL Server MVP的Kimberly Tripp在他的文章里讨论了VLF大小的影响,并提供了如何恰当管理VLF大小的指导——事务日志VLF—太多仍是太少?数据库设计
相反,若是日志文件只有几个很大的VLF,咱们有长时间占用大块日志的风险。每一个VLF都有很大数量的日志记录,SQL Server不能截断VLF直到它没有包含活动日志。这个状况下截断会因某些缘由延迟(在缺乏日志空间重用部分会详细谈到),这会致使日志的快速增加。例如,咱们假设每一个VLF是1GB大小且日志满了。你进行了一第二天志备份,可是全部的VLF包含活动日志的一部分,SQL Server不能截断日志。它没有别的选择只能增长更多VLF,若是日志的增加率设置为同等大小,那么日志增加会很快,直到有VLF变成可截断。
所以,正确设置日志初始大小很是重要,那它的增加才会是合适的大小步骤,最小化日志碎片也避免了过快增加。
正确设置初始大小且可控制它的增加的第2个缘由是:对于日志文件,每一个增加是相对昂贵的操做。数据和日志文件增加超时是正常的。SQL Server能够优化增长新数据文件和扩展示有数据文件的过程,经过即时文件初始化(instant file initialization)(在SQL Server 2005引入,容许在磁盘上分配空间给数据文件,而不须要进行填零)。遗憾的是,对于日志文件是不同的,对于日志文件建立或增加的空间分配,仍是须要初始化且填零。
为何事务日志不能使用即时初始化?
进一步关注事务日志填零,看下Paul Randal的文章:http://sqlskills.com/BLOGS/PAUL/post/Search-Engine-QA-24-Why-cant-the-transaction-log-use-instant-initialization.aspx
若是你经历事务日志的不可控增加,它因为要么是活动日志频率过高,要么是有因素阻止日志文件里的空间重用,或者二者都有。
若是增加的主要缘由是活动日志过多,你要检查下是否能够避免这个活动,例如调整处理大容量数据和索引维护的数据库模式,这样的话这些操做不会完整记录(例如针对这些操做使用大容量日志恢复模式)。可是,若是日志备份里包含有任何的最小化日志操做,大容量操做会当即阻止数据库到时间点的恢复(能够阅读下第6篇文章来得到更多详细信息)。若是这是不可接受的,你必须直接接受大日志的事实,根据具体状况计划它的增加和管理(例如日志备份频率),在接下来的稳当的日志管理会介绍。
若是增加缘由是缺乏日志空间的重用,你要找出什么阻止这个重用并采起措施来修正这个问题。
索引维护操做是个很常见的致使事务日志使用率过分和增加的缘由,尤为数据库运行在完整恢复模式里。进行索引维护须要日志空间量取决于下列因素:
当重建索引时,无论在线仍是离线,使用ALTER INDEX REBUILD,或是已经废弃的SQL Server 2000里的DBCC DBREINDEX,SQL Server新建立一个索引的副本,而后一旦重建完整,删除老的副本(这是为何你至少须要数据文件里索引大小同样的可用空间)
日志记录和在线索引重建
在SQL Server 2008和后续版本,在线索引重建是完整日志操做,在SQL Server 2005里是最小化日志。所以,在后续SQL Server版本上进行这样的操做本质上须要更多的事务日志空间,能够看下:https://support.microsoft.com/zh-cn/kb/2407439 还有Kalen Delaney的日志,对于完整和大容量日志2个恢复模式,验证下在在线和离线索引重建期间的日志记录。
在完整恢复模式里,索引重建能够是很是占资源的操做,须要事务日志里的不少空间。在简单或大容量日志恢复模式里,重建索引是最小化日志操做,这意味着只有分配被记录,实际的页并没改变,所以经过这个操做减小了日志空间量。
若是你切换到简单模式进行索引重建,LSN链会当即中断。你只能恢复你的数据库到刚才事务日志备份里的包含的时间点。为了从新开始日志链,你须要切换回完整恢复模式并当即进行一次完整或差别数据库备份。
若是你切换到大容量日志模式(看下第6篇),LSN链仍是连续的,但还会影响到进行时间点的恢复,由于包含最小化日志操做的日志备份不能用来恢复到时间点。若是能恢复到时间点的要求是至高无上的,那么索引重建或任何最小化日志操做不要使用大容量日志恢复模式。除非在数据库里没有同时发生的用户活动,你可使用。否则的话,在可能的状况下考虑在完整恢复模式里进行索引重建。
若是使用的是大容量日志模式,尽量使时间点恢复的时间最小,这样能够最小化暴露数据丢失风险。为了作到这一点,在完整模式里进行日志备份,切换到大容量日志,进行索引重建,而后切换回完整进行另外一个日志备份。
最后一个重点要记住的是ALTER INDEX REBUILD操做是在一个单独事务里。若是索引很大,事务的执行时间会很长,在期间,这会阻止日志里的空间重用。这就是说,即便你在简单模式里重建索引,你也要想到自检查点(CHECKPOINT)操做后日志应该保持很小,重建是最小化日志,在剧烈的重建期间,日志文件还会快速扩展。
和重建索引相比,使用ALTER INDEX REORGANIZE或者SQL Server里的DBCC INDEXDEFRAG(已废弃)重组(碎片整理)索引都是完整记录操做,无论是任何恢复模式,所以实际的页修改总被记录。可是,一般索引重组比索引重建须要更少的日志空间,尽管这是索引里下降碎片的一个功能;比起轻度碎片,重度碎片索引会须要更多的日志空间来重组。
另外,ALTER INDEX REORGANIZE操做是经过多个更短的事务完成的。所以,当与按期的日志备份相结合(或在简单恢复模式里)时,在此操做期间,日志空间能够被重用,所以要求操做期间日志空间最小化。
例如,对于重建操做,重建20GB的索引会须要超过20GB的空间,由于它发生在一个单独的事务里。可是,重组20GB的索引会须要更少的空间,由于在重组里每一个页分配修改是个单独的事务,所以日志记录能够用按期日志备份截断,让日式空间能够重用。
若是你的组织对任何潜在数据丢失不能容忍的,那么你没有选择,只能让全部的数据库运行在完整恢复模式里,而且稳当计划你的日志大小和增加。所以索引重建是做为一个单独线程发生的,日志至少会和你重建的索引同样的大小。如刚才所说,索引重组会须要更少的空间,且容许在操做期间经过日志备份来截断日志。这样的话,为了同时避免日志暴涨,可行的话,你能够用日志重组。
若是你的SLA和操做级别协议(Operational Level Agreements(OLAs))容许一些潜在的数据丢失,那么在索引重建前l切换到大容量日志恢复模式能够为重建索引最小化空间需求量。可是,要在最小化数据丢失的方式下进行,例如已经讨论确认过了。
无论使用的恢复模式,你能够经过重组索引而不是重建索引来在日志上最小化索引维护操做的影响。能够的话,能够看下微软的指导方针,为了最小化索引维护操做的影响,对于绝大数状况,并非全部状况,决定何时进行索引重建,何时进行索引重组(查看索引重组和重建)。他们也声明:对于碎片级别大于5%且小于30%,你应该重组索引,对于碎片级别大于30%,你应该重建它。
可是,在索引维护期间,在保护日志过分增加里,最有效的武器是维护那些真正须要的索引。使用SSMS维护计划向导,索引维护是个背注一掷的操做:要么重建(或重组)数据库里(维护计划里的全部数据库)的全部索引,要么全不维护。一个更好的方法是使用sys.dm_db_index_physical_stats的DMV来看下碎片程度根据须要来决定索引重建/重组策略。
Ola Hallengren的免费维护脚本
Ola Hallengren提供一个综合的免费维护工具,它展现了如何使用sys.dm_db_index_physical_stats进行索引分析来进行智能维护,它能够用来代替SSMS里向导建立的数据库维护计划(https://ola.hallengren.com/)。
可是最好的方法,是计划只维护那些能够在查询上提供真正持久影响的索引。逻辑碎片(在乱序中的索引页)挫败了SQL Server的预读机制(https://msdn.microsoft.com/zh-cn/library/ms191475%28v=sql.105%29.aspx),且使在磁盘上读取连续页I/O-效率更低。可是,这只真正影响从磁盘的大范围扫描。即便对很是大碎片的索引,若是你不扫描表,重建或重组索引不会提升性能。下降页深度(经过页分裂或删除形成的不少缺口)会带来更多的页占用磁盘空间,且在内存里,会须要更多的I/O带宽来传输数据。再说一次,这个碎片格式不会真正影响不频繁修改的索引,所以重建它们不会有帮助。
计划索引维护前,问下本身什么性能标准从维护受益?它会大会减小I/O?它会提升你最昂贵查询的多少性能?它是持久正面影响么?若是这些答案是“否”或“不知道”,那么按期索引维护可能不是个长远的答案。最后,值得注意的是对小索引维护是不值得的。一般引用的阈值是近1000页。在微软,当Paul Randal管理存储引擎开发团队时,建议这些值做为参考,在在线帮助里记录了。注意,尽管这只是个建议并不对全部环境合适,如Paul在他的博客文章里谈到的:“在线帮助的索引碎片阈值来自哪里?”
sys.dm_tran_database_transactions的DMV提供在事务日志上事务活动影响的有用内部信息。在他们的书里,《使用SQL Server动态管理视图进行性能调优》,获得他们的容许后,复制在这里,做者Louis Davidson和Tim Ford,演示了如何使用这个DMV和一些其余的,来调查可能形成事务日志过分增加的事务。
在代码7.1里的例子重用来自第6篇的FullRecovery数据库和PrimaryTable_Large表。在一个显性事务里,它重建了汇集索引而后调查日志增加。
1 USE FullRecovery 2 GO 3 BEGIN TRANSACTION 4 5 ALTER INDEX ALL ON dbo.PrimaryTable_Large REBUILD 6 7 SELECT DTST.[session_id], 8 DES.[login_name] AS [Login Name], 9 DB_NAME (DTDT.database_id) AS [Database], 10 DTDT.[database_transaction_begin_time] AS [Begin Time], 11 DATEDIFF(ms, DTDT.[database_transaction_begin_time], GETDATE()) 12 AS [Duration ms] , 13 CASE DTAT.transaction_type 14 WHEN 1 THEN 'Read/write' 15 WHEN 2 THEN 'Read-only' 16 WHEN 3 THEN 'System' 17 WHEN 4 THEN 'Distributed' 18 END AS [Transaction Type], 19 CASE DTAT.transaction_state 20 WHEN 0 THEN 'Not fully initialized' 21 WHEN 1 THEN 'Initialized, not started' 22 WHEN 2 THEN 'Active' 23 WHEN 3 THEN 'Ended' 24 WHEN 4 THEN 'Commit initiated' 25 WHEN 5 THEN 'Prepared, awaiting resolution' 26 WHEN 6 THEN 'Committed' 27 WHEN 7 THEN 'Rolling back' 28 WHEN 8 THEN 'Rolled back' 29 END AS [Transaction State], 30 DTDT.[database_transaction_log_record_count] AS [Log Records], 31 DTDT.[database_transaction_log_bytes_used] AS [Log Bytes Used], 32 DTDT.[database_transaction_log_bytes_reserved] AS [Log Bytes RSVPd], 33 DEST.[text] AS [Last Transaction Text], 34 DEQP.[query_plan] AS [Last Query Plan] 35 FROM sys.dm_tran_database_transactions DTDT 36 INNER JOIN sys.dm_tran_session_transactions DTST 37 ON DTST.[transaction_id] = DTDT.[transaction_id] 38 INNER JOIN sys.[dm_tran_active_transactions] DTAT 39 ON DTST.[transaction_id] = DTAT.[transaction_id] 40 INNER JOIN sys.[dm_exec_sessions] DES 41 ON DES.[session_id] = DTST.[session_id] 42 INNER JOIN sys.dm_exec_connections DEC 43 ON DEC.[session_id] = DTST.[session_id] 44 LEFT JOIN sys.dm_exec_requests DER 45 ON DER.[session_id] = DTST.[session_id] 46 CROSS APPLY sys.dm_exec_sql_text (DEC.[most_recent_sql_handle]) AS DEST 47 OUTER APPLY sys.dm_exec_query_plan (DER.[plan_handle]) AS DEQP 48 WHERE DB_NAME(DTDT.database_id) = 'FullRecovery' 49 ORDER BY DTDT.[database_transaction_log_bytes_used] DESC; 50 -- ORDER BY [Duration ms] DESC; 51 COMMIT TRANSACTION
(代码7.1:调查重日志写入事务)
(插图7.1:索引重建后日志活动结果)
顺便提下,若是咱们用ALTER INDEX...REORGANIZE来运行这个例子,那么在Log Bytes Used列的值会从近159M降为近0.5M。
若是你怀疑缺乏日志空间重用形成了日志增加,你的第一个任务是找出什么阻止了重用。开始经过查询如代码7.2所示的
,看下对于提到的数据库log_reuse_wait_desc的列值错误信息是什么。sys.databases
1 SELECT name , 2 recovery_model_desc , 3 log_reuse_wait_desc 4 FROM sys.databases 5 WHERE name = 'FullRecovery'
(代码7.2:检查下log_reuse_wait_desc的列值)
log_reuse_wait_desc的列值会展现为何当前空间不被重用的缘由。若是你已经执行刚才的例子(代码7.1),那么极可能FullRecovery数据库在这列会显示LOG_BACKUP值(下面会详谈)。
阻止日志重用不止一个。sys.databases视图只显示其中一个缘由。所以它是解决问题的一个可能方法,再次查询sys.database会看到log_reuse_wait不一样的缘由。
在在线帮助里列出了log_reuse_wait_desc全部可能值,但在这里咱们只谈最多见的缘由,解释如何安全确保那个空间能够被重用。
若是从sys.databases查询,log_reuse_wait_desc的返回值是LOG_BACKUP,那么你极可能遭受完整或大事务日志的最多见缘由,即在完整恢复模式里的数据库(或次之,大容量日志恢复模式),没有进行事务日志备份。
在SQL Server的不少版本里,model数据库默认是完整恢复模式。由于model数据库是建立全部新SQL Server用户数据库的模板,新的数据库继承自model的配置。
对于大多数生产数据库,使用完整恢复模式是推荐的作法,由于它容许数据库的时间点恢复,最小化灾难事件的数据丢失。可是,接下来的常见错误是调整备份策略是只有完整备份(或者有差别备份)而没有按期的事务日志备份。这个策略有2个大问题:
为了进行时间点的恢复并控制日志大小,咱们必须用数据库完整或完整和差别备份连同事务日志备份。对于咱们的FullRecovery数据库,咱们能够进行日志备份,如代码7.3所示,而后再次查询sys.databases。
1 USE master 2 GO 3 BACKUP LOG FullRecovery 4 TO DISK = 'D:\SQLBackups\FullRecovery_log.trn' 5 WITH INIT 6 GO 7 8 SELECT name , 9 recovery_model_desc , 10 log_reuse_wait_desc 11 FROM sys.databases 12 WHERE name = 'FullRecovery'
(代码7.3:解决日志备份问题)
若是缺乏日志备份是日志增加问题的缘由,首先要作的是验证问题数据库是否真的须要运行在完整恢复模式。若是必需要能恢复数据库到任意时间点或到灾难事件前的一个时间点,则是必须的,或者必需要用完整恢复模式的另外一个缘由(例如数据库镜像)。若是在SLA里目标恢复点( Recovery Point Objective (RPO) )为最大15分钟的数据丢失,那么极可能你不能只进行完整数据库备份和差别数据库备份,必需要进行日志备份。
可是,若是由于不须要而没有进行日志备份,那么数据库不该该运行在完整恢复模式;咱们能够切换数据库到简单恢复模式,那事务日志的不活动部分会自动标记为可重用,在检查点的时候。
若是数据库须要运行在完整恢复模式,那么开始日志备份,或调查下备份须要的频率。事务日志的备份频率取决于不少因素,例如数据修改的频率,还有在灾 难中,SLA上可接受的数据丢失程度。另外,你应该采起措施保证日志增加是可控的,在未来是可预见的,在这篇文章里的稳当的日志管理部分会介绍。
若是log_reuse_wait_desc的返回值是ACTIVE_TRANSACTION,那么你受到来自SQL Server里完整或大的事务日志的第二个常见缘由:长时间运行或未提交的事务。从新执行下来自代码7.1的事务,但不提交,在从新执行下代码7.3,你 会看到这个值返回(不要忘记回去提交这个事务)。
如在第2篇日志截断和空间重用部分介绍的,事务日志里的VLF只有在不包含活动日志部分时才会被截断。若是数据库试用完整或大容量日志恢复模式,只 有日志备份操做才能够进行截断。数据库里长时间运行的事务延迟包含事务开始后生成的日志记录的VLF的截断,包括其它并发事务对数据库里的数据修改产生的 日志记录,甚至当这些改变还没提交时。另外,长时间运行的事务的空间需求量会经过对“补偿日志记录”保留的空间增长,若是在系统里事务回滚的话,这些日志 记录就会产生。这些保留是须要的,保证在回滚期间,这些事务能够成功恢复而不会用完日志空间。
另外一个常见对log_reuse_wait_desc值的活动事务值是“孤立”的显式事务,它莫名其妙的从不提交。容许用户在事务里输入的应用程序就特别容易是这类问题。
形成长时间运行的事务的最多见操做,也是在数据库里生成大量日志记录,是从数据库里归档或清除数据。数据保持每每是数据库设计里过后的想法,常常是数据库已经活跃一段时间后才考虑,是在服务器接近可用存储的容量限制。
一般,当须要归档时,第一个反应是从数据库里使用简单的DELETE语句删除不须要的数据,如代码7.4所示。为了生成一些简单的测试数据,这个脚本使用Jeff Moden的随机数据生成器的简化版本,简单修改来生成日期到2012。
USE FullRecovery ; GO IF OBJECT_ID('dbo.LogTest', 'U') IS NOT NULL DROP TABLE dbo.LogTest ; SELECT TOP 500000 SomeDate = CAST(RAND(CHECKSUM(NEWID())) * 3653.0 + 37534.0 AS DATETIME) INTO dbo.LogTest FROM sys.all_columns ac1 CROSS JOIN sys.all_columns ac2 ; -- delete all but the last 60 days of data DELETE dbo.LogTest WHERE SomeDate < GETDATE() - 60
(代码7.4:大容量数据删除)
取决于要删除的在日期范围内存在的行数,这会变成引发日志增加问题的长时间运行的事务,即便数据库运行在简单恢复模式。外键串联约束的出现或审计触 发器会恶化问题。若是其它表引用目标表,经过外键约束来级联删除,那么SQL Server页经过级联约束来记录删除的行的细节。若是表上有DELETE触发器,在触发器执行期间,SQL Server也会记录进行的操做。
为了最小化在事务日志上的影响,数据清理应该简化为更短,独立的事务。有不少方法中断长时间运行的事务为小的批处理。若是表存在级别约束或DELETE触发器,咱们能够在循环内进行删除操做,在一个时间删除一天的数据,若是代码7.5所示。注意,在这个简单的例子里,在咱们的表里没有足够的行来验证这个技术的使用,简单的DELETE;清理几百万行数据更合适。也注意批量删除的主要关心的是并非速度(代码7.5会比代码7.4运行更慢)。最要关心的是避免日志过分增加和锁升级。
DECLARE @StopDate DATETIME , @PurgeDate DATETIME SELECT @PurgeDate = DATEADD(DAY, DATEDIFF(DAY, 0, MIN(SomeDate)), 0) , @StopDate = DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()) - 60, 0) FROM dbo.LogTest WHILE @PurgeDate < @StopDate BEGIN DELETE dbo.LogTest WHERE SomeDate < @PurgeDate SELECT @PurgeDate = DATEADD(DAY, 1, @PurgeDate) END
(代码7.5:将数据清除拆散为小的事务)
使用这个方法清除数据,每一个删除事务的持续时间是从表里删除一条记录的时间,加上任何触发器或级联约束进行它们操做的时间。若是数据库使用简单恢复模式,下个检查点会截断这些清除产生的日志记录,只要在VLF里没有相关数据清理的活动日志存在。
当在处理过程当中级联约束或审计触发器不是要考虑的,咱们可使用不一样的方法来清理表上的数据,同时最小化事务。不是进行一点的DELETE操做,它会影响多少的数据,取决于指定日期里存在的行数,在DELETE语句里使用TOP运算符会限制每一个循环操做影响的行数。使用@@ROWCOUNT来捕获DELETE操做影响的行数,运算符会在小的批处理语句里清除数据,直到@@ROWCOUNT的值小于DELETE语句里TOP子句里指定的行数,如代码7.6所示。
这个方法只有在没有触发器和级别约束时使用有效,否则的话@@ROWCOUNT的结果不是实际表删除的行数,而是触发器执行或经过强制级联约束影响的行数。
1 DECLARE @Criteria DATETIME , 2 @RowCount INT 3 SELECT @Criteria = GETDATE() - 60 , 4 @RowCount = 10000 5 WHILE @RowCount = 10000 6 BEGIN 7 DELETE TOP ( 10000 ) 8 FROM dbo.LogTest 9 WHERE SomeDate < @Criteria 10 SELECT @RowCount = @@ROWCOUNT 11 END
(代码7.6:对于数据清理在DELETE语句里使用TOP运算符)
这些方法在SQL Server 2000,2005,2008的任何版本均可以使用,在数据清理期间最小化事务。
可是,若是你的数据库是SQL Server 2005或2008企业版,且常常清理数据,那么清理数据的更好方法是表分区,在列上筛选要删除的数据。这会更小影响事务日志,由于分区包含的数据会从表转出并清理,对SQL Server只是记录区从新分配的操做。
管理存档
这已是这个系列文章讨论范围以外了,自动归档方案。可是,一个可能的归档过程涉及分区,表之间的架构复制,容许一个表的一个分区能够转出到另外一个。在主要的OLTP表最小化数据的活动部分,但只减小修改的元数据的归档过程。Kimberley Tripp已经写了一份具体的白皮书,叫作SQL Server 2005里的表和索引分区,它谈了划窗技术(sliding window technique)。
默认状况下,SQL Server会在隐性事务里包裹任何数据修改语句来保证,在灾难事件里,SQL Server能够回滚在故障点已经作出的修改,返回数据到一致的状态。若是修改为功,隐性事务会提交到数据库。和自动发生的隐性事务相比,咱们建立显性事务,在代码包裹多个修改在一个事务里,来保证全部的修改经过ROLLBACK命令能够撤销,或者经过COMMIT命令提交让它持久。
当恰当使用时,显性事务能够保证多个表之间的数据修改做为一个单位成功完成,或者所有都不修改。当使用不当时,无论怎样,在数据库里孤立的事务仍是活跃的,阻止事务日志的截断,这会致使事务日志增加或填满。在SQL Server里有不少孤立事务的缘由,这超出了这篇文章详细介绍的范围。可是,一些常见的缘由有:
一旦一个事务开始,它会保持活动直到建立的链接,事务触发COMMIT或ROLLBACK语句,或者链接从SQL Server中断(当使用绑定的连接,会容许会话共享锁,这是个异常)。
如今的应用程序一般会使用链接池,在池里保持与SQL Server的链接让程序重用,即便当程序代码在链接上调用Close()方法。当对孤立事务进行故障排除是理解最后一点很是重要,由于即便链接在加入或返回到应用程序链接池前被重置,数据库里打开的事务仍是继续存在的,若是它们没有正常结束的话。
事务相关的DMV提供大量的额外信息,无论当前事务的状态和进行的操做。可是,一些DBA仍是使用DBCC OPENTRAN做为识别是否为孤立事务(或只是长时间运行的)为形成日志增加的根源的最快方法。
在DBCC OPENTRAN(DatabaseName)格式里会接受数据库名称做为输入参数,数据库名称是用做检查打开事务的数据库名。若是数据库里有活动事务存在,命令会输出相似以下的信息。
1 DBCC OPENTRAN (FullRecovery)
Transaction information for database 'FullRecovery'. Oldest active transaction: SPID (server process ID): 56 UID (user ID) : -1 Name : user_transaction LSN : (897:15322:1) Start time : Sep 18 2012 1:01:29:390PM SID : 0x010500000000000515000000fd43461e19525f12828ba628ee0a0000 DBCC execution completed. If DBCC printed error messages, contact your system administrator.
(代码7.7:来自DBCC OPENTRAN的输出信息范例)
DBCC OPENTRAN只输出最先的活动事务,但主要表示事务是否为活动的疑问是开始时间。通常来讲,未提交的事务是打开很长时间才会是形成事务日志增加的缘由。
另外一个重要的信息是SPID(server process ID;在DMV里这用session_id代替),这用来标识建立打开事务的会话。咱们能够经过SPID判断事务是真的孤立仍是只是长时间运行的,经过查询sysprocesses视图(在SQL Server 2000里)或者SQL Server 2005及后续版本里的sys.dm_exec_sessions和sys.dm_exec_connections的动态视图,如代码7.8所示。注意sysprocesses视图在SQL Server 2005及后续版本仍是可用的,保持向后的兼容性。在运行代码7.8时,在每一个查询里,直接用你看到的会话值替换session_id值(咱们注释了几列,只是为了简化输出的可读性)。
1 USE master 2 GO 3 SELECT spid , 4 status , 5 -- hostname , 6 -- program_name , 7 -- loginame , 8 login_time , 9 last_batch , 10 ( SELECT text 11 FROM :: 12 fn_get_sql(sql_handle) 13 ) AS [sql_text] 14 FROM sysprocesses 15 WHERE spid = 53 16 17 USE FullRecovery 18 GO 19 SELECT s.session_id , 20 s.status , 21 -- s.host_name , 22 -- s.program_name , 23 -- s.login_name , 24 s.login_time , 25 s.last_request_start_time , 26 s.last_request_end_time , 27 t.text 28 FROM sys.dm_exec_sessions s 29 JOIN sys.dm_exec_connections c ON s.session_id = c.session_id 30 CROSS APPLY sys.dm_exec_sql_text(c.most_recent_sql_handle) t 31 WHERE s.session_id = 53
(代码7.8:使用DMV来识别孤立的仍是长时间运行的事务)
若是回话是runnable,running或suspended状态,那么可能问题的根源是长时间运行,而不是孤立的事务。可是,只有进一步的调查才能确认。颇有可能刚才的事务失败且链接重置,使用链接池,当前运行的语句不是打开事务所关联的。
在SQL Server 2005和后续版本,咱们可使用sys.dm_tran_session_transactions和sys.dm_tran_database_transactions对打开的事务收集信息,包括事务开始事件,打开事务使用的日志数,以及日志空间使用字节数,如咱们刚才代码7.1所见。代码7.9展现了一个简单的版本,带有范例输出。
(代码7.9:收集打开事务的信息)
除非应用程序设计来检查,处理孤立的事务,清除事务的惟一方法是KILL会话,它会形成事务回滚,和链接中断同样,在下一第二天志备份期间,容许日志里的空间是能够被重用的。可是,回滚的执行后果必需要理解的。
除了刚才提到的缘由以外,还有其余一些问题阻止日志里空间重用,致使日志过分增加。这里我会谈其中的一些,这个问题的更多信息,能够看下Gail Shaw的文章,为何个人事务日志满了?
REPLICATION
在事务复制期间,日志读取代理的任务是读取事务日志, 查找关联修改的日志记录,复制到订阅者(例如,“待定的复制”)。一旦修改被复制,会标记日志为“已复制”。缓慢或延迟的日志读取活动会致使记录剩为“待 定的复制”很长时间,在此期间它们仍是活动日志的一部分,所以母VLF不能被截断。对于经过变动数据捕获( Change Data Capture (CDC))功能须要的日志记录也有相似的问题存在。
无论任何状况,sys.databases的 log_reuse_wait_desc列会显示REPLICATION做为问题根源。在事务磁盘阵列的输出性能里,这个问题自己也暴露了瓶颈。尤为是, 在并发写加载下的延迟读取操做。写入日志文件会持续发生,但用日志读取代理相关的和日志备份文件读取的读操做也要持续的。同一时间有持续的读和写发生,取 决于系统中的日志活跃级别和活动日志部分的大小,会致使磁头随机的I/O活动,由于磁头须要改变位置来读取活动日志的头,而后活动日志的尾。咱们可使用性能监视器(PerfMon)里磁盘计数器 Physical Disk\Disk Reads/sec 和 Physical Disk\Disk Writes/sec来故障排除这类问题,看下SQL Server的故障排除的免费电子书的第2章来进一步了解这个问题的细节:https://www.simple-talk.com/books/sql-books/troubleshooting-sql-server-a-guide-for-the-accidental-dba/
这些复制等待问题的故障排除的第一步是识别日志读取器,SQL 代理做业是否正常运行。若是不是的话,尝试启动它们。若是启动失败,你要找出为何。
若是做业是运行的,可是复制一直等待,事务日志快速增加,你须要找到一些方法让相关的日志标记为“已复制”,这样的话它们的母VLF能够被重用。遗憾的是,没有完美的解决方案来避免复制或在CDC环境里的反作用,但你能够尝试下面方法中的一种。
记住直接切换到简单恢复模式,但愿能截断日志,是不行的,由于复制和CDC2个均不支持简单恢复模式,仍是继续须要日志读取器直到日志读取器的SQL代理处理完成处理。
快照复制架构改变问题
在SQL Server 2005里使用快照复制有一个已知的问题,当架构修改时,它会致使应该标记为复制的架构修改没被标记。这个问题能够看下这个文章的解决方法:http://blogs.msdn.com/b/sqlserverfaq/archive/2009/06/01/size-of-the-transaction-log-increasing-and-cannot-be-truncated-or-shrinked-due-to-snapshot-replication.aspx
当log_reuse_wait_desc column列显示为ACTIVE_BACKUP_OR_RESTORE做为当前等待描述,长时间运行的数据库完整或差别备份是最有可能致使日志重用问题。在数据库完整或差别备份期间,备份过程会延迟日志截断,这样的话事务日志的活动部分会被包含为完整备份的一部分。在备份操做没完成期间,容许修改到数据库的页,当备份用WITH RECOVERY恢复时,可让数据库恢复到一致的状态。若是这样的等待形成持续的问题,你会须要调查下优化备份过程的方式,例如提升备份性能(提供备份压缩)或者提升硬盘I/O系统的性能。
当log_reuse_wait_desc column列显示为DATABASE_MIRRORING,做为当前等待描述,异步数据库镜像操做可能致使日志重用问题。
在异步镜像里,主上的事务只有一旦提交,相关的日志记录才会传输到镜像数据库。对于异步数据库镜像,主的日志不能截断直到日志记录已传输。当镜像问题发生时,主上大量的日志记录会保持为活动日志的一部分,阻止日志空间重用,直到复制到镜像完成。
对于异步数据库镜像,若是镜像不可用咱们会看到DATABASE_MIRRORING,归因于断开或很是慢的链接,或镜像会话的挂起。对于异步数据库镜像,在正常操做和链接问题期间,咱们会看到这个值。
在这个状况下,首先我会检查下受影响数据库的镜像会话状态。若是它们没有正确同步,那么你须要在主和镜像之间故障排除失败链接的缘由。数据库镜像一个最多见的缘由,当证书用来保证安全终端时,是证书过时,须要从新颁发证书。进一步讨论镜像链接问题处理已经不是这个文章的讨论范围,除非数据库已经正常同步,那么日志记录会发送到镜像,在主上事务日志的活动部分会继续增加,不能截断直到中断镜像配置。
若是在主上的日志率大大超过能够传送到镜像的日志率,那么主上的日志会快速增加。若是镜像服务器用来作报表,经过建立快照,对镜像验证磁盘I/O配置没有饱和,经过刚才提升的性能监视器里硬盘计数器。若是这是问题所在,中止镜像服务器的服务器能够临时解决问题。若是问题是严格的大量事务,数据库没有运行在SQL Server 2008或更高,那么升级能够解决问题,由于可使用SQL Server 2008或更高版本的日志流压缩。
最好的方法是判断镜像问题的缘由并解决它。例如,调优生成大量日志记录的操做,例如大容量加载数据,或者重组索引,在操做期间能够减小对系统的影响。
最坏的状况,事务日志管理不当或突发、快速的日志增加会形成事务日志增加,最后吞食完硬盘上全部可用空间。到这个时候就不能增加了,你会遇到9002错误,事务日志满错误,数据库会变成只读。
尽管这个问题很紧迫,冷静面对很重要,避免这类接下来会提到的”无心识“的解决方法,处理不当或作不应作。显然当前的问题是让SQL Server能够继续写日志,经过生成更多可用空间。若是原由是缺乏日志备份,第一个要作的是从新运行代码7.1;若是log_reuse_wait_desc列返回值是 Log Backup,那么和可能这是问题缘由。一个在MSDB数据库里对backupset表的查询,如代码7.10所示,会确认是否要在数据库上进行一第二天志备份,还有上一第二天志备份的时间。
1 USE msdb ; 2 SELECT backup_set_id , 3 backup_start_date , 4 backup_finish_date , 5 backup_size , 6 recovery_model , 7 [type] 8 FROM dbo.backupset 9 WHERE database_name = 'DatabaseName'
(代码7.10:哪一个备份已作,何时作的)
在type列,D表明数据库备份,L表明日志备份,I表明差别备份。若是没有日志备份,或者它们并不频繁,那么你最好的作法是进行一第二天志备份(这里假定数据库运行在完整或大容量日志恢复模式)。但愿,这个能释放日志里的实在空间,而后你能够进行合适的日志备份计划和日志增加管理策略。
若是由于某些缘由不能进行日志备份,例如磁盘空间不足,或者进行日志备份的时间超过可接受的问题解决时间,那么,取决于对问题数据库的灾难恢复策略,或许能够经过临时切换到简单恢复模式来强制日志截断,这样在检查点的时候日志中不活动的VLF会被截断。而后你能够切换回完整数据库恢复模式,进行新的完整数据库备份(或差别备份,这里假定先前已经有一次完整备份)来从新开始用于时间点恢复的日志链。固然,你仍是充分调查问题,来保证空间不会再次直接吞食完。还有记住这点,刚才讨论过的,若是阻止空间重用的问题不是日志备份,那么这个技术就无效了,由于这些记录会保留在活动日志里,阻止截断。
若是缺乏日志备份不是问题,或者进行完日志备份不能解决问题,那么调查缘由可能会花更多的时间。最快和最简单的方法是在日志硬盘上增长更多的空间。这表示要清理掉其余文件,或者增长当前日志硬盘的容量,或者在不一样的硬盘列里增长额外日志文件,但这会占用你一点喘息的空间,你须要让数据库摆脱只读模式,而后进行一第二天志备份。
若是日志备份释放空间失败,你要找出什么阻止了日志里的空间重用。调查下sys.databases(代码7.1)来找出什么阻止了日志空间重用,采起合适的行动,如刚才缺乏日志空间重用部分介绍的。
若是这个啥都没透露,你须要进一步调查找出什么操做形成过分日志致使日志增加,如事务日志过分增加部分介绍的。
最后,解决了任何空间重用问题,极可能咱们的日志文件会在磁盘上占用很大的空间。做为一次性的测量,例如假定咱们采起措施保证往后日志增加有妥善的管理(下一部分就会谈到),是可使用DBCC SHRINKFILE来回收臃肿事务日志文件使用的空间。在第8篇咱们会提供如何作的例子。
咱们要么指定收缩日志的文件target_size,要么指定0位目标大小,让日志收缩的尽量小,而后当即使用ALTER DATABASE来调整到合适的大小。后者是推荐的方法,它会最小化日志文件的碎片。碎片问题是你应该从不按期进行的DBCC SHRINKFILE任务的主要缘由,由于它只用来控制日志大小;咱们会在下个部分详细讨论这个。
遗憾的是,在网络上搜索”事务日志满“会返回大量论坛的帖子,博客文章,甚至不少复制于SQL Server网站的文章,那些建议矫正的方法,坦白说,很危险。咱们在这里会谈其中一些流行的建议。
这个方法,你清理了全部用户的数据库,分离数据库(或者关闭它),删除日志文件(或重命名),而后从新附加数据库,会引发新的日志文件建立,它的大小由model数据库决定。这能够说是处理完整事务日志的最可怕的方式。它会形成数据库启动失败,数据库为RECOVERY_PENDING状态。
取决于数据库在日志删除时是否正常关闭,在数据库正常部分的恢复阶段,数据库可能不能进行撤销和重作操做,由于事务日志已经丢失,不能返回数据库为一致的状态。当日志文件丢失时,数据库须要事务日志来进行故障恢复,数据库不能正常启动,只能从最近的可用备份里恢复数据库,这就会致使数据丢失。
建立,分离,附加,修复可疑数据库
在特定状况下,能够黑入现存的数据库的配置,容许事务日志重建,但这会破坏数据库里现有数据库的完整性。这类操做是,最好是最后实在绝对无法恢复数据库数据了,这是咱们这个系列文章不推荐的作法。至于如未尝试黑入数据库来看已删除的事务日志,能够看下Paul Randal的文章:建立,分离,附加,修复可疑数据库。
在SQL Server 2000 和2005,BACK LOG WITH TRUNCATE_ONLY是SQL Server支持的强制截断事务日志的方法,在数据库运行在完整或大容量日志模式。使用这个命令实际不会作日志内容备份副本;在截断VLF里的记录会忽略。所以,不像正常日志备份,你在破坏你的LSN链,你只能恢复数据库到先前任何日志备份里的时间点。还有,即便数据库设置为完整恢复模式,实际上,从那个点开始,会运行在自动截断模式,在检查点会继续截断不活动的VLF。为了让数据库运行在完整恢复模式,从新开始LSN链,你须要进行一次完整(或差别)备份。
没有意识到对灾难恢复的影响的人们才会常用这个命令,在SQL Server 2005里已经废弃,从SQL Server 2008开始已经移除这个命令了。遗憾的是,这个技术更阴险的版本,仍是继续被支持,取而代之这个命令,那就是BACKUP LOG TO DISK='NUL',NUL是忽略任何数据写入的“虚拟文件”。这个技术的真正扭曲是,不像BACKUP LOG WITH TRUNCATE_ONLY,SQL Server无论日志记录,直接忽略。就SQL Server而言,进行日志备份后,日志记录会在备份文件里安全存储,这样的话,LSN链是完整的,在活动日志里的不活动VLF能够安全截断。任何接下来,惯例的日志备份会成功,但从故障恢复的角度来讲是无用的,由于日志备份文件丢失的话,数据库只能恢复到上次标准日志备份的时间点,在BACKUP LOG TO DISK='NUL'命令发出前。
不要使用这里的任何技术。强制日志截断的正确方法是临时切换数据库导简单恢复模式,如前所述。
如在处理事务日志满错误部分讨论的,事务日志在不多状况下是由于管理不当形成的,日志增加正被活动管理,使用DBCC SHRINKFILE来回收事务日志占用的空间是个能够接受的操做。
但咱们毫不能把日志收缩做为平常,计划维护操做的一部分。缘由是咱们每次收缩日志,它会为接下来的事务当即再次增加来存储日志记录。如在日志大小和增加部分讨论的,事务日志不能利用即时文件初始化,所以全部日志增加引起SQL Server须要分配的存储空间填零操做。另外,若是咱们依赖事务日志自动增加(下部分会谈到),在日志文件了会汇集更多的VLF,这个日志碎片会影响任何须要读取这个日志文件的进程性能,若是碎片实在太多,也会影响到数据修改性能。
对于事务日志文件的最佳作法是预先设置好它的合适大小,这样的话正常状况下就不会增加。而后,监视它的使用率来决定是否须要人为增加,容许你决定合适的增加大小且决定要添加到日志文件里的VLF的大小和个数。在第8篇咱们会具体讨论。
没有任何意想不到的操做或问题而致使不正常的日志增加(复制问题,未提交的事务等等),若是事务日志关联的数据库运行在完整恢复模式,还一直增加,其实只有2个缘由:
最好的作法,若是你不能经过减小它们之间的时间来增长日志备份的频率,当在加载的时,能够人为增长日志文件大小而不是让它自动增加,而后恢复原来大小。有大的咱们人为增加的事务日志文件,但有最小化数量的VLF并非个坏事,即便大部分时间日志文件有空余空间。咱们会在第8篇详细讨论这个。
对于SQL Server数据库的操做,事务日志很是重要,还有在灾难事件里能最小化数据丢失风险。在日志疯狂增加的状况里,甚至满了,DBA须要快速诊断并解决问题,同时要保持冷静也很是重要,避免不深思熟虑的反应,例如强制日志截断,还有计划按期的日志收缩,这只会弊大于利。
很是感谢SQL Server故障排除的做者Jonathan Kehayias,电子书也是能够下载的,为本文提供了大量参考。
也感谢您这么耐心围观完这篇文章,我真是挤牙膏同样,熬了一个星期才能出一篇文章,感谢您的关注!!!