web-dev-qa-db-ja.com

仮想ログファイルが常に順番に割り当てられないのはなぜですか?

四半期ごとに、githubの tigertoolbox/Fixing-VLFs / からのクエリを使用して、CMSを介してすべてのサーバーのVLF=を確認します。これには提案が含まれます(およびコード)を使用して、検出された内容を修正します。調整を行う前に、常に何が起こっているのかを完全に理解するように努めます。90%の確率でVLF私が適用する解決策は推奨事項と同じではありませんが、通常は近いです。

DBCC LOGINFOの使用VLFのいくつかが順番に使用されていないことがわかりました。その理由を理解しようとしています。これ 非常に投票された回答; BACKUP LOG TO DISK の後でさえ、ログファイルのDBCC SHRINKFILEがサイズを縮小しないと、それが発生する可能性があると言いましたが、理由はわかりません。

仮想ログファイルは常に順番に割り当てられるわけではないため、

これは Jonathan Kehayias;と矛盾するようです。 1日のXEvent(31/23)–仕組み–複数のトランザクションログファイル

130 VLFの2つ(51行目と52行目)のみが現在使用されていることがわかります。最大のFSeqNoは41808で、130 = 41678を減算します。41678未満のFSeqNoは表示されません。したがって、おそらくすべてが最後の130 VLFロールオーバーで使用されました。

108〜109行目を見ると、110行目が最初に書き込まれ、次に109行目、108行目が書き込まれていることがわかります。また、パリティがオフになっています(シナリオに何が追加されるかは不明です)。注文。

enter image description here

シーケンスの順序を乱す原因となったものが通過すると、次にVLFを介して、作成された順に書き込まれると思います。なぜそうではないのですか?

一貫性を示すために、86行目が両方の画像に含まれていることに注意してください。 enter image description here

enter image description here

この例はSQL 2014サーバーからのもので、データベースは現在完全復旧しており、1年以上経過しています。サーバーは3ノードAGの一部です。上記の結果は、tログが1時間ごとにバックアップされるセカンダリサーバーから取得されます。最後の10の増加は、1000 MBの自動拡張によって発生しました(2014以降では1 VLFが作成されます)。現在、ログファイルの初期サイズは115,000MB、自動拡張は1,000MBです。

編集Max Vernon回答のリンクをありがとうございます。私はそれらが順不同で書き込まれる方法を理解しましたが、私は、ブロッキングイベントがクリアされた後、それらが順不同で書き込まれ続けると仮定しています。 FSegNoを逆方向にたどることで、それがどのように発生したかを再現できます。行52は、FSeqNo 41808を含む現在のVFLです。行35では、FSeqNoは41791です。FSeqNo41790は、行130(最後の行)にあります。おそらく、FSeqNo 41790の作成時にVFL 130が作成されたと考えられます。しかし、次のVLFへの書き込みのイベントが40,000あり、130 VLFこれは、ログ全体で300サイクルを超えることになります。

数時間後、リストはこれに見えます

enter image description here

6
James Jenkins

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

出力:

╔════════════╦═════════════════════╗
║name║recovery_model_desc║
╠════════════╬═════════════════════╣
║TestVLFSeq║FULL║
╚════════════╩═════════════════════╝
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

出力:

╔════════════════╦════════╦══════════╦════════════ ═╦════════╦════════╦════════╦═══════════╗
║RecoveryUnitId║FileId ║FileSize║StartOffset║FSeqNo║ステータス║パリティ║CreateLSN║
╠════════════════╬════════╬═════ ═════╬═════════════╬════════╬════════╬════════╬═══ ════════╣
║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

次に、最初のクエリウィンドウに戻り、データベースとログをバックアップして、クリアされたVLFをクリアします。

BACKUP DATABASE TestVLFSeq TO DISK = 'NUL:' WITH INIT;
BACKUP LOG TestVLFSeq TO DISK = 'NUL:' WITH INIT;
GO

BACKUP DATABASEは厳密には必須ではありませんが、なんといっても "エーテル"にバックアップしています)。

ここで、DBCC LOGINFOを確認すると、次のことがわかります。

╔════════════════╦════════╦══════════╦════════════ ═╦════════╦════════╦════════╦═══════════╗
║RecoveryUnitId║FileId ║FileSize║StartOffset║FSeqNo║ステータス║パリティ║CreateLSN║
╠════════════════╬════════╬═════ ═════╬═════════════╬════════╬════════╬════════╬═══ ════════╣
║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║
╚════════════════╩════════╩══════════╩══════════ ═══╩════════╩════════╩════════╩═══════════╝

最初と2番目のVLFはクリアされました。テーブルに別の64行を挿入して、DBCC LOGINFOを確認すると、

INSERT INTO dbo.TestData DEFAULT VALUES
 /* insert 64 rows - ~512 KB of log usage */
GO 64

DBCC LOGINFO
GO

私たちは見る:

╔════════════════╦════════╦══════════╦════════════ ═╦════════╦════════╦════════╦═══════════════════╗
║RecoveryUnitId║FileId║FileSize║StartOffset║FSeqNo║ステータス║パリティ║CreateLSN║
╠════════════════╬══════ ══╬══════════╬═════════════╬════════╬════════╬════ ════╬═══════════════════╣
║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 286 286720║1810432║0║0║0║38000000039500007║
╚═══════════ ═════╩════════╩══════════╩═════════════╩════════╩═ ═══════╩════════╩═══════════════════╝

かなり明確に、「シーケンス外」のVLFがあります。ただし、SQL ServerはFSeqNoを使用してリカバリ関連の操作を実行するため、VLFが物理的に「順序どおり」ではないことは問題ではありません。

何らかの理由で、プログラムでVLFがFSeqNoの順序になっていると予想される場合は、次のように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;

"strange" VLF配置の場合、次の出力が表示されます。

╔════════════════╦════════╦══════════╦════════════ ═╦════════╦════════╦════════╦═══════════════════╗
║RecoveryUnitID║FileID║FileSize║StartOffset║FSeqNo║ステータス║パリティ║CreateLSN║
╠════════════════╬══════ ══╬══════════╬═════════════╬════════╬════════╬════ ════╬═══════════════════╣
║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 286 286720║1810432║0║0║0║38000000039500007║
╚═══════════ ═════╩════════╩══════════╩═════════════╩════════╩═ ═══════╩════════╩═══════════════════╝

「2」のStatusでマークされたVLFは、VLFは再利用できないことを示します。次のクエリを使用して、ログの再利用を妨げている原因を特定できます。

SELECT d.name
    , d.recovery_model_desc
    , d.log_reuse_wait_desc
FROM sys.databases d
WHERE d.name = N'TestVLFSeq';

上記の例のように、データベースに開いているトランザクションがあるときにこのコードを実行すると、出力は次のようになります。

╔════════════╦═════════════════════╦══════════════ ═══════╗
║名前║recovery_model_desc║log_reuse_wait_desc║
╠════════════╬═══════════ ══════════╬═════════════════════╣
║TestVLFSeq║FULL║ACTIVE_TRANSACTION║
 ╚════════════╩═════════════════════╩══════════════ ═══════╝

log_reuse_wait_desc列には、 Microsoft Transaction Log Documentation から取得した次の可能な値を含めることができます。

  1. CHECKPOINT-最後のログの切り捨て以降、チェックポイントが発生していないか、ログの先頭が仮想ログファイル(VLF)を超えていない。すべての復旧モデルに適用されます。 SQL Server 2008からSQL Server 2017までに適用されます。

    これは、ログの切り捨てを遅らせるための日常的な理由です。

    データベースにメモリが最適化されたデータファイルグループがある場合、log_reuse_wait列がチェックポイントまたはxtp_checkpointを示すことを期待する必要があります。

    問題のデータベースのコンテキスト内でCHECKPOINT T-SQLコマンドを使用してチェックポイントを強制できます。

  2. LOG_BACKUP-トランザクションログを切り捨てる前に、ログのバックアップが必要です。完全または一括ログ復旧モデルにのみ適用されます。 SQL Server 2008からSQL Server 2017に適用

    BACKUP LOG <database> TO ...を実行して、ログのバックアップを作成します。

  3. 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';
    
  4. ACTIVE_TRANSACTION-SQL Server 2008からSQL Server 2017に適用

    コミットまたはロールバックされていない開いているトランザクションがあります。この開いているトランザクションはアクティブに実行されていない可能性があることに注意してください。トランザクションを開いてクライアントで長時間実行アクションを実行し、最後にトランザクションをコミットするのは、不十分に記述されたコードである可能性があります。

    以下を検討してください。

    • ログバックアップの開始時に、長時間実行されているトランザクションが存在する可能性があります。この場合、スペースを解放するには、別のログバックアップが必要になる場合があります。長時間実行されるトランザクションは、単純な復旧モデルを含むすべての復旧モデルでのログの切り捨てを防止します。単純な復旧モデルでは、トランザクションログは通常、各自動チェックポイントで切り捨てられます。

    • トランザクションは延期されます。遅延トランザクションは、リソースが使用できないためにロールバックがブロックされるアクティブなトランザクションです。遅延トランザクションの原因と、それらを遅延状態から移動する方法については、「 遅延トランザクション 」を参照してください。

    • 長時間実行されるトランザクションもtempdbのトランザクションログをいっぱいにする可能性があります。 Tempdbは、ソート用の作業テーブル、ハッシュ用の作業ファイル、カーソル作業テーブル、行のバージョン管理などの内部オブジェクトのユーザートランザクションによって暗黙的に使用されます。ユーザートランザクションにデータの読み取りのみが含まれている場合でも(SELECTクエリ)、内部オブジェクトが作成され、ユーザートランザクションの下で使用されます。その後、tempdbトランザクションログを入力できます。

  5. DATABASE_MIRRORING-データベースミラーリングが一時停止されているか、高パフォーマンスモードでは、ミラーデータベースがプリンシパルデータベースより大幅に遅れています。完全復旧モデルにのみ適用されます。 SQL Server 2008からSQL Server 2017に適用

    Status = 2のVLFが多数表示され、データベースミラーリングが有効になっている場合は、データベーストランザクションがミラーに適時に適用されていることを確認してください。

  6. REPLICATION-トランザクションレプリケーション中に、パブリケーション関連のトランザクションがディストリビューションデータベースに配信されていません。完全復旧モデルにのみ適用されます。 SQL Server 2008からSQL Server 2017までに適用されます。

  7. 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;
    
  8. LOG_SCAN-これはルーチンであり、通常はログの切り捨てが遅れる一般的な簡単な原因です。

  9. AVAILABILITY_REPLICA-Always On可用性グループのセカンダリレプリカは、このデータベースのトランザクションログレコードを対応するセカンダリデータベースに適用しています。 SQL Server 2012からSQL Server 2017までに適用されます。

  10. OLDEST_PAGE-SQL Server 2012〜SQL Server 2017に適用

    データベースが間接チェックポイントを使用するように構成されている場合、データベースの最も古いページがチェックポイントログシーケンス番号(LSN)より古い可能性があります。この場合、最も古いページがログの切り捨てを遅らせる可能性があります。 (すべての復旧モデル)

    間接チェックポイントの詳細については、「 データベースチェックポイント(SQL Server) 」を参照してください。

  11. OTHER_TRANSIENT-この値は現在使用されていません。 SQL Server 2012からSQL Server 2017までに適用

  12. XTP_CHECKPOINT-データベースが復旧モデルを使用し、メモリ最適化データファイルグループがある場合、log_reuse_wait列がチェックポイントまたはxtp_checkpointを示しているはずです。 -SQL Server 2014からSQL Server 2017に適用

11
Max Vernon

115GBのログファイルがあり、1時間ごとにバックアップされます。 VLFはすべて約1GBであり、自動拡張は、1つの新しいVLF各拡張イベントで1GBを作成するように設定されています。 現在のサイズの1/8未満の増加を伴う2014インスタンス

広範な検索といくつかのテストを行った後、次の次のVLFがまだステータス2である場合を除いて、ログファイルは常に順番に(1、2、3など))書き込みます。次の書き込みが発生します次に利用可能なステータス0 VLF、または存在しない場合は自動拡張が発生します。

時間ごとのt-logバックアップと順不同FSeqNoの場合、t-logバックアップが実行された後、最近VLFがステータス2に残った(long running incomplete)108、109、110行目で確認された順序で、2つのバックアップを介してリリースされない一部のVLFが可能です。

Msdb..backupsetのバックアップサイズを確認して詳細を取得します。これはセカンダリでのAGバックアップであるため、セカンダリサーバーで次のコマンドを実行します。

    --Find how big were the logs when backed up.  
    SELECT database_name
    , CONVERT(varchar, CAST ((backup_size/1024/1024) AS MONEY), 1) as 'BackUpSizeMB'
    , user_name as 'BackUpBy'
    , backup_start_date
    , backup_finish_date
    , recovery_model
    , CONVERT(varchar, CAST ((compressed_backup_size/1024/1024) AS MONEY), 1) as 'compressed_backup_sizeMB'
    FROM msdb..backupset
    WHERE backup_finish_date > DATEADD(MONTH, -12, CURRENT_TIMESTAMP) -- If want month(s)
    --WHERE backup_finish_date > DATEADD(DAY, -1, CURRENT_TIMESTAMP)  -- or only day(s)
    And type = 'L' --Log
    and database_name = 'MyDB'
    order by backup_start_date desc
    --order by backup_size desc

    /*
    Backup type. Can be:
    D = Database
    I = Differential database
    L = Log
    F = File or filegroup
    G = Differential file
    P = Partial
    Q = Differential partial
    Can be NULL.
    */

あなたはおそらくこのようなものを見つけるでしょう

enter image description hereenter image description hereenter image description here

100GB前後の定期的なバックアップがあり、どちらの側にもかなり大きなバックアップがあります。私のインスタンスでは、これらの大規模イベント以外のほとんどのt-logバックアップが約500MBであることがわかりました。何かがおそらく2時間実行され、多くのバックアップでVLFが保持されます。その後、アクティビティが非常に低くなり、1つのVLFのみがクリアされ、次の。問題の構成では、見つけられると予想される単純なシーケンスを返すために、使用率が低い状態で数日かかる場合があります。

次に、自動成長をチェックする必要があります Kendraには、チェック方法を示すブログ投稿があります これは、最近のイベントがあった場合にのみ機能しますが、このようなものを見つける可能性があります。

enter image description here

「最適」VLF構成は50以下VLFそれぞれ500MB(または512)以下)であることを示唆するいくつかのブログ投稿があります。明らかに、約25GBまでのログで機能します。

質問からのシナリオで。各VLFは1GBであり、1時間ごとのバックアップがあります。追加の順不同FSeqNoは、いくつかのVLFがバックアップ時にロックされていることを示します。この答え(同じデータベースと問題のインスタンスからOPによって投稿された)83GBのt-logバックアップが完了する前に、1分で10GBの自動拡張があります。

自動拡張が発生しないようにするのが最適です。これらは、システムにすでに負荷がかかっている場合にのみ発生します。これに対処するには、予測される事前成長がオフピークで発生する、定期的なイベントに対応するための十分なスペースが必要です。

3つのオプションがあります。

  1. ログの事前拡張(バックアップ履歴を使用して必要なスペースを見積もる
  2. より頻繁にバックアップ
  3. 同じ合計ログファイル領域を使用して、さらに小さいVLF=を作成します。

このオプションは、環境内のシステム構成全体を考慮して選択する必要があります。単一の正しい選択はありません。

TL:DR1時間ごとのバックアップでも、VLFが再利用されないようにロックしているものがあります。これは、現在の構成のひずみを示している可能性があります。問題を特定し、それが繰り返し発生するイベントかどうかを判断し、適切に対処することをお勧めします。

4
James Jenkins