Jam*_*ins 6 sql-server sql-server-2012 transaction-log
每季度,我通过 CMS 使用来自github 上的tigertoolbox/Fixing-VLFs/的查询检查我所有服务器上的 VLF 。这包括用于更正所发现内容的建议(和代码)。在进行任何调整之前,我总是尝试充分了解正在发生的事情。我应用的 VLF 解决方案在 90% 的情况下与建议不同,尽管它通常很接近。
使用DBCC LOGINFO
我发现有几个 VLF 没有按顺序使用。我试图理解为什么。这个高度投票的答案; 即使在 BACKUP LOG TO DISK说它可能发生后,日志文件上的 DBCC SHRINKFILE 也不会减小大小
,但不是为什么。
因为虚拟日志文件并不总是按顺序分配的,
这似乎与Jonathan Kehayias相冲突;一天的 XEvent(31 个中的 23 个)——它是如何工作的——多个事务日志文件
我们可以看到,在 130 VLF 中,目前仅使用了两个(第 51 和 52 行)。最大的 FSeqNo 是 41808,减去 130 = 41678。我没有看到任何低于 41678 的 FSeqNo,所以大概在最近的 130 次 VLF 翻转中都使用了。
如果我们查看第 108 - 109 行,我们会看到第 110 行首先被写入,然后是第 109 行,然后是第 108 行。而且奇偶校验关闭(不确定这会增加场景的内容)LSN 显示它们是在相反的位置创建的命令。
我希望一旦导致顺序中断的原因过去了,下一次通过 VLF 将按创建的顺序写入。为什么不是?
该示例来自 SQL 2014 服务器,数据库目前处于完全恢复状态,并且已持续一年或更长时间。该服务器是 3 节点 AG 的一部分,以上结果取自辅助服务器,其中 t-logs 每小时备份一次。最后 10 次增长是通过 1000MB 的自动增长发生的(2014+ 创建了 1VLF)。日志文件当前的初始大小为 115,000MB,自动增长为 1,000MB。
编辑 Max Vernon 感谢您提供答案中的链接。我理解它们是如何乱序写入的,但我假设一旦阻塞事件被清除,它们就会继续乱序写入。通过向后跟踪 FSegNo,您可以重新创建它是如何发生的。第 52 行是 FSeqNo 41808 的当前 VFL。在第 35 行,FSeqNo 是 41791。FSeqNo 41790 在第 130 行(最后一行)。据推测,VFL 130 是在编写 FSeqNo 41790 时创建的。但是已经有 40,000 个写入下一个 VLF 的事件,其中 130 个 VLF 将超过 300 个周期的日志。
几个小时后,列表看起来像这样
Han*_*non 11
Paul Randal 在这篇博文中详细介绍了日志 VLF 可能会按预期顺序分配的情况。
从本质上讲,当现有的 VLF 无法重用时,即使在简单模式下,日志文件增长也可能导致意外的分配顺序。
你为什么想知道分配顺序?VLF 按FSeqNo
顺序使用,无论是在写入新日志记录时,还是在执行回滚和前滚恢复操作时。
在完全恢复模型中使用数据库并不能防止无序的 VLF 分配顺序。我创建了一个最低限度完整的可验证示例:
SET NOCOUNT ON;
USE master;
GO
IF EXISTS (SELECT 1 FROM sys.databases d WHERE d.name = N'TestVLFSeq')
BEGIN
ALTER DATABASE TestVLFSeq SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE TestVLFSeq;
END
GO
CREATE DATABASE TestVLFSeq
ON (NAME = 'system', FILENAME = 'C:\temp\TestVLFSeq.mdf', SIZE = 10MB, FILEGROWTH = 10MB, MAXSIZE = 100MB)
LOG ON (NAME = 'log', FILENAME = 'C:\temp\TestVLFSeq.ldf', SIZE = 1MB, FILEGROWTH = 1MB, MAXSIZE = 10MB);
GO
ALTER DATABASE TestVLFSeq SET RECOVERY FULL;
BACKUP DATABASE TestVLFSeq TO DISK = 'NUL:';
GO
SELECT d.name
, d.recovery_model_desc
FROM sys.databases d
WHERE d.name = N'TestVLFSeq';
GO
Run Code Online (Sandbox Code Playgroud)
输出:
??????????????????????????????????????? ? 姓名 ?recovery_model_desc ? ??????????????????????????????????????? ? 测试VLFSeq ? 满的 ? ???????????????????????????????????????
USE TestVLFSeq;
GO
CREATE TABLE dbo.TestData
(
someVal varchar(8000) NOT NULL
CONSTRAINT DF_TestData
DEFAULT ((CRYPT_GEN_RANDOM(8000)))
);
GO
INSERT INTO dbo.TestData DEFAULT VALUES
/* insert 64 rows - ~512 KB of log usage */
GO 64
DBCC LOGINFO
GO
Run Code Online (Sandbox Code Playgroud)
输出:
?????????????????????????????????????????????????????? ????????????????????????????????????????????? ? 恢复单元 ID ? 文件 ID ? 文件大小 ?起始偏移量?FSeqNo ? 地位 ?平价?创建LSN? ?????????????????????????????????????????????????????? ????????????????????????????????????????????? ? 0 ? 2 ? 253952?8192?34 ? 2 ? 64 ? 0 ? ? 0 ? 2 ? 253952?262144?35 ? 2 ? 64 ? 0 ? ? 0 ? 2 ? 253952?516096?36 ? 2 ? 64 ? 0 ? ? 0 ? 2 ? 278528?770048?0 ? 0 ? 0 ? 0 ? ?????????????????????????????????????????????????????? ?????????????????????????????????????????????
正如您在上面的输出中看到的,我们分配了 3 个 VLF。
现在,在单独的查询窗口中,运行此命令以创建即使日志备份也无法清除的日志记录:
USE TestVLFSeq;
BEGIN TRANSACTION
INSERT INTO dbo.TestData DEFAULT VALUES
GO
Run Code Online (Sandbox Code Playgroud)
现在,切换回第一个查询窗口,并备份数据库和日志以清除任何已清除的 VLF:
BACKUP DATABASE TestVLFSeq TO DISK = 'NUL:' WITH INIT;
BACKUP LOG TestVLFSeq TO DISK = 'NUL:' WITH INIT;
GO
Run Code Online (Sandbox Code Playgroud)
(这BACKUP DATABASE
不是严格要求的,但到底是什么,我们无论如何都要备份到“以太”)。
现在,当我们检查时DBCC LOGINFO
,我们看到:
?????????????????????????????????????????????????????? ????????????????????????????????????????????? ? 恢复单元 ID ? 文件 ID ? 文件大小 ?起始偏移量?FSeqNo ? 地位 ?平价?创建LSN? ?????????????????????????????????????????????????????? ????????????????????????????????????????????? ? 0 ? 2 ? 253952?8192?34 ? 0 ? 64 ? 0 ? ? 0 ? 2 ? 253952?262144?35 ? 0 ? 64 ? 0 ? ? 0 ? 2 ? 253952?516096?36 ? 2 ? 64 ? 0 ? ? 0 ? 2 ? 278528?770048?0 ? 0 ? 0 ? 0 ? ?????????????????????????????????????????????????????? ?????????????????????????????????????????????
第一个和第二个 VLF 已清除。如果我们现在在表中插入另外 64 行,并查看DBCC LOGINFO
:
INSERT INTO dbo.TestData DEFAULT VALUES
/* insert 64 rows - ~512 KB of log usage */
GO 64
DBCC LOGINFO
GO
Run Code Online (Sandbox Code Playgroud)
我们看:
?????????????????????????????????????????????????????? ??????????????????????????????????????????????????? ? 恢复单元 ID ? 文件 ID ? 文件大小 ?起始偏移量?FSeqNo ? 地位 ?平价?创建LSN? ?????????????????????????????????????????????????????? ??????????????????????????????????????????????????? ? 0 ? 2 ? 253952?8192?38 ? 2 ? 128?0 ? ? 0 ? 2 ? 253952?262144?35 ? 0 ? 64 ? 0 ? ? 0 ? 2 ? 253952?516096?36 ? 2 ? 64 ? 0 ? ? 0 ? 2 ? 278528?770048?37 ? 2 ? 64 ? 0 ? ? 0 ? 2 ? 253952?1048576?39 ? 2 ? 64 ? 38000000039500007 ? ? 0 ? 2 ? 253952?1302528?0 ? 0 ? 0 ? 38000000039500007 ? ? 0 ? 2 ? 253952?1556480?0 ? 0 ? 0 ? 38000000039500007 ? ? 0 ? 2 ? 286720?1810432?0 ? 0 ? 0 ? 38000000039500007 ? ?????????????????????????????????????????????????????? ???????????????????????????????????????????????????
很明显,我们有“乱序”的 VLF。但是,由于 SQL Server 使用FSeqNo
来执行与恢复相关的操作,因此 VLF 在物理上不“按顺序”并不重要。
如果出于某种原因,您以编程方式希望 VLFFSeqNo
有序,您可以这样查询DBCC LOGINFO;
:
DECLARE @cmd nvarchar(max);
IF OBJECT_ID(N'tempdb..#loginfo', N'U') IS NOT NULL
DROP TABLE #loginfo;
CREATE TABLE #loginfo
(
RecoveryUnitID int
, FileID int
, FileSize int
, StartOffset bigint
, FSeqNo int
, [Status] tinyint
, Parity tinyint
, CreateLSN bigint
);
SET @cmd = 'DBCC LOGINFO;';
INSERT INTO #loginfo
EXEC sys.sp_executesql @cmd;
SELECT *
FROM #loginfo li
ORDER BY /* put "0" FSeqNo rows at the end */
CASE
WHEN li.FSeqNo = 0 THEN CONVERT(int, POWER(CONVERT(bigint,2),31) - 1)
ELSE li.FSeqNo
END;
Run Code Online (Sandbox Code Playgroud)
对于“奇怪的” VLF 排列,它具有以下输出:
?????????????????????????????????????????????????????? ??????????????????????????????????????????????????? ? 恢复单元 ID ? 文件 ID ? 文件大小 ?起始偏移量?FSeqNo ? 地位 ?平价?创建LSN? ?????????????????????????????????????????????????????? ??????????????????????????????????????????????????? ? 0 ? 2 ? 253952?262144?35 ? 0 ? 64 ? 0 ? ? 0 ? 2 ? 253952?516096?36 ? 2 ? 64 ? 0 ? ? 0 ? 2 ? 278528?770048?37 ? 2 ? 64 ? 0 ? ? 0 ? 2 ? 253952?8192?38 ? 2 ? 128?0 ? ? 0 ? 2 ? 253952?1048576?39 ? 2 ? 64 ? 38000000039500007 ? ? 0 ? 2 ? 253952?1302528?0 ? 0 ? 0 ? 38000000039500007 ? ? 0 ? 2 ? 253952?1556480?0 ? 0 ? 0 ? 38000000039500007 ? ? 0 ? 2 ? 286720?1810432?0 ? 0 ? 0 ? 38000000039500007 ? ?????????????????????????????????????????????????????? ???????????????????????????????????????????????????
标有Status
“2”的 VLF 表示 VLF 不能重复使用。您可以使用以下查询确定阻止日志重用的原因:
SELECT d.name
, d.recovery_model_desc
, d.log_reuse_wait_desc
FROM sys.databases d
WHERE d.name = N'TestVLFSeq';
Run Code Online (Sandbox Code Playgroud)
如果在数据库中有一个打开的事务时运行此代码,如上例所示,您将看到输出如下所示:
?????????????????????????????????????????????????????? ????????? ? 姓名 ?recovery_model_desc ? log_reuse_wait_desc ? ?????????????????????????????????????????????????????? ????????? ? 测试VLFSeq ? 满的 ?ACTIVE_TRANSACTION ? ?????????????????????????????????????????????????????? ?????????
该log_reuse_wait_desc
列可以具有以下可能的值,取自Microsoft 事务日志文档:
CHECKPOINT
- 自上次日志截断后未发生检查点,或者日志的头部尚未移动到虚拟日志文件 (VLF) 之外。适用于所有恢复模型。适用于 SQL Server 2008 到 SQL Server 2017。
这是延迟日志截断的常规原因。
当数据库具有内存优化数据文件组时,您应该会看到 log_reuse_wait 列指示 checkpoint或xtp_checkpoint。
您可以CHECKPOINT
在相关数据库的上下文中使用T-SQL 命令强制执行检查点。
LOG_BACKUP
- 在可以截断事务日志之前需要进行日志备份。仅适用于完整或大容量日志恢复模型。适用于 SQL Server 2008 到 SQL Server 2017
运行BACKUP LOG <database> TO ...
以创建日志的备份。
ACTIVE_BACKUP_OR_RESTORE
- 适用于 SQL Server 2008 到 SQL Server 2017
使用以下查询查看当前正在运行的备份:
SELECT d.name
, der.command
, der.start_time
, der.percent_complete
FROM sys.dm_exec_requests der
INNER JOIN sys.databases d ON der.database_id = d.database_id
WHERE der.command = N'BACKUP DATABASE'
OR der.command = N'BACKUP LOG';
Run Code Online (Sandbox Code Playgroud)ACTIVE_TRANSACTION
- 适用于 SQL Server 2008 到 SQL Server 2017
存在尚未提交或回滚的打开事务。请注意,这个打开的事务可能没有主动运行——它可能是编写得很差的代码,它打开一个事务然后在客户端执行一些长时间运行的操作,最后提交事务很晚。
考虑以下:
日志备份开始时可能存在长时间运行的事务。在这种情况下,释放空间可能需要另一个日志备份。请注意,长时间运行的事务在所有恢复模式下都可以防止日志截断,包括简单恢复模式,在这种模式下,事务日志通常在每个自动检查点上被截断。
交易被推迟。延迟事务实际上是一个活动事务,其回滚由于某些不可用资源而被阻止。有关延迟事务的原因以及如何将它们移出延迟状态的信息,请参阅延迟事务。
长时间运行的事务也可能填满 tempdb 的事务日志。Tempdb 由用户事务隐式用于内部对象,例如用于排序的工作表、用于散列的工作文件、游标工作表和行版本控制。即使用户事务仅包括读取数据(SELECT 查询),也可以在用户事务下创建和使用内部对象。然后可以填充 tempdb 事务日志。
DATABASE_MIRRORING
- 数据库镜像暂停,或者在高性能模式下,镜像数据库明显落后于主体数据库。仅适用于完整恢复模式。适用于 SQL Server 2008 到 SQL Server 2017
如果您看到大量状态 = 2 的 VLF,并且启用了数据库镜像,请确保及时将数据库事务应用于镜像。
REPLICATION
- 在事务复制期间,发布相关事务尚未传送到分发数据库。仅适用于完整恢复模式。适用于 SQL Server 2008 到 SQL Server 2017。
DATABASE_SNAPSHOT_CREATION
- 适用于 SQL Server 2008 到 SQL Server 2017
正在创建数据库快照。检查以下有关 SQL Server 上存在的快照的详细信息:
SELECT SnapshotDBName = d.name
, SnapshotCreateDate = d.create_date
, SourceDBName = d_source.name
, SourceCreateDate = d_source.create_date
FROM sys.databases d
INNER JOIN sys.databases d_source ON d.source_database_id = d_source.database_id
WHERE d.source_database_id IS NOT NULL
ORDER BY d.create_date;
Run Code Online (Sandbox Code Playgroud)LOG_SCAN
- 这是延迟日志截断的例行程序,通常是简短的原因。
AVAILABILITY_REPLICA
- Always On 可用性组辅助副本正在将此数据库的事务日志记录应用到相应的辅助数据库。适用于 SQL Server 2012 到 SQL Server 2017。
OLDEST_PAGE
- 适用于 SQL Server 2012 到 SQL Server 2017
如果数据库配置为使用间接检查点,则数据库中最旧的页面可能比检查点日志序列号 (LSN) 旧。在这种情况下,最旧的页面可以延迟日志截断。(所有恢复模型)
有关间接检查点的信息,请参阅数据库检查点 (SQL Server)。
OTHER_TRANSIENT
- 当前未使用此值。适用于 SQL Server 2012 到 SQL Server 2017
XTP_CHECKPOINT
- 当数据库使用恢复模型并具有内存优化数据文件组时,您应该会看到 log_reuse_wait 列指示检查点或xtp_checkpoint。- 适用于 SQL Server 2014 到 SQL Server 2017
归档时间: |
|
查看次数: |
1087 次 |
最近记录: |