これは、SQLServerトランザクションログとその特殊性に関するシリーズの2番目の記事です。ここでは、ログレコードの詳細を調べます。
ログレコード
ログレコードは、ログと回復のメカニズムの中核です。ログレコードは、データベース内の1つの変更を記述します。したがって、データベースへのすべての変更には、その特定の変更を説明するのに役立つ1つまたは複数のログレコードがあります。ログレコードの詳細を理解する必要はありませんが、ログとリカバリで何が起こっているのかを理解するには、これらの詳細は非常に興味深いものです。
ログレコードには、最初の記事で定義した一意のログシーケンス番号があります。ログシーケンス番号を使用すると、ログレコードをトランザクションログで見つけることができます。さらに、すべてのデータファイルページのページヘッダーには、変更がページに反映されている最新のログレコードを識別するLSNがあります。これはクラッシュリカバリにとって重要です。
同時トランザクションのログレコードは、時間内に発生した時期に応じてトランザクションログに混在します。ログレコードは、ディスクにフラッシュされるまで、バッファプールのログブロックに保存されます。
ユーザーデータベースまたはシステムデータベースには、ログに記録されない操作はありません。ただし、例外があります。tempdbでは、バージョンストアと作業ファイルの操作はログに記録されません。ログレコードがトランザクションログに移動することはありません。
ログレコードの内容は何ですか?
ログレコード内の情報により、ログレコードをやり直し(ロールフォワード)または元に戻す(ロールバック)ことができます。ログレコードのこの機能は、トランザクションをロールバックできるようにするため、およびリカバリ作業のために重要です。ログレコードには、ログレコードのタイプに応じて、多くのフィールドが含まれます。次のような、すべてのレコードに共通するフィールドがいくつかあります。
- ログレコードタイプ
- トランザクションログレコードの開始
- トランザクションログレコードをコミットする
- 割り当てビットマップを変更してページを割り当てる
- 行を挿入する
- 行の削除
- 行の変更
- ログレコードのコンテキスト 、もしあれば。
- ログレコードが含まれるトランザクションID もしあれば。ほとんどのログレコードはトランザクションの一部です。
- ログレコードの長さ 。通常、ログレコードのサイズは固定されており、ログレコードに含まれるデータの量に応じて、可変部分もあります。
- 同じトランザクションの前のログレコードのLSN 。もしあれば。 LSNは基本的に、その特定のトランザクションによって生成された以前のトランザクションログレコードへのポインタです。以前のLSNのこのチェーンでは、ロールバックが最新のログレコードから開始して逆の順序で実行されるため、特定のトランザクションをロールバックできます。
- 予約されたログスペースの量 ログレコードを元に戻す必要がある場合に備えて。
ログスペースの予約
トランザクションの転送部分で生成されるすべてのログレコードは、トランザクションログを拡大することなく、ログレコードをロールバックできるように、トランザクションログに空き領域を予約する必要があります。
ログスペース予約メカニズムは非常に保守的であり、予期しない状況が発生した場合に備えて、常に十分なスペースを予約し、通常はそれ以上のスペースを予約します。たとえば、圧縮されたテーブルでの更新または削除は、非圧縮テーブルでの同様の更新または削除よりも多くのスペースを予約します。これは、圧縮テーブルでの更新のロールバックが、圧縮ページ上にない更新された行に対処する必要があるため、圧縮データではなく全幅の列をログレコードに書き込む必要があるために発生します。
>ログレコードタイプ
ログレコードには、次のような多くの種類があります。
- LOP_FORMAT_PAGE ログ形式のページ操作—ページがフォーマットされた場所です。つまり、ヘッダーが作成されています。ログレコードは、少なくともページヘッダーをログに記録し、インデックスのビルドや再構築などの操作の一部としてページが作成および入力されている場合は、ページのコンテンツをさらにログに記録する可能性があります)
- LOP_MODIFY_ROW この操作は、既存のデータのごく一部を変更しています。
- LOP_SET_BITS このログレコードは、割り当てビットマップに適用されます。
- LOP_INSERT_ROWSおよびLOP_DELETE_ROWS
- LOP_SET_FREE_SPACE PFSに適用–ページの割り当てステータスを追跡するビットマップの割り当て。
表形式のインデックスのデータページまたはインデックスページに変更を加えるログレコードには、次のものが含まれます :
-
- アロケーションユニットID
- ページ上のレコードのページIDとスロットID。これは基本的に、ページ上のデータまたはインデックスレコードのゼロベースのレコードIDです。
- 変更されたデータの残像、または残像と残像。 1つのログレコードにこれらの複数のセットが含まれる場合があります。アフターイメージにより、やり直しを行うことができます。変更前の画像を使用すると、元に戻すことができます。
ログのロック
一部のログレコードには、説明されている変更が行われたときにロックが保持されていたビットマップが含まれています。ビットマップには次のものが含まれます:
-
-
- ロック数のカウント。
- ロックの種類とモード-たとえば、Xモードでのページロック。
- ロックの内容
-
クラッシュリカバリおよびデータベースミラーリング/可用性グループのフェイルオーバー中に、これらのロックは、取り消されるすべてのログレコードに対して取得されます。これにより、SQLServer2005以降のEnterpriseEditionの高速リカバリ機能が可能になります。
トランザクションのログレコード
すべてのトランザクションは、常に次の順序で、少なくとも3つのログレコードを生成します。
-
-
-
- LOP_BEGIN_XACT – SPID、トランザクション名、開始時刻などの情報が含まれます。 SQL Serverによって開始されるすべてのトランザクションには、操作を説明する名前があります(AllocFirstPage、DROPOBJなど)
- トランザクションのその他のレコード。
- LOP_COMMIT_XACT –トランザクションがコミットした場合。
- LOP_ABORT_XACT –トランザクションがロールバックした場合。
-
-
これらには両方ともトランザクションの終了時刻が含まれます。
トランザクションのログレコードは、LSNによって逆方向にリンクされます。これは、トランザクションに対して生成される次のログレコードには、この特定のトランザクションに対して生成された前のログレコードのLSNがあることを意味します。これにより、トランザクションを正しくロールバックできます。次のような一部のログレコードは、まったくトランザクションではありません。
-
-
-
- PFSの空き領域の変更(他のトランザクションとの調整は不可能)
- 差分ビットマップの変更(一方向の変更のみ)
-
-
ログレコードの調査
ログレコードを調べるには2つの方法があります。 DBCC LOGINFO関数を使用できますが、fn_dblogテーブル値関数を使用することをお勧めします。構文は非常に単純です:
SELECT * FROM fn_dblog (startLSN, endLSN); GO
それは次の理由で非常に強力な機能です:
-
-
-
- 簡単に管理できる表形式の結果セットを返します。
- 複雑な述語を使用できます。
- 最も古いコミットされていないトランザクションの開始から最新のログレコードまで、ログのアクティブな部分のすべてのトランザクションログをスキャンします。これは、トレースフラグ2537を使用してオーバーライドできます
-
-
startLSNフィールドとendLSNフィールドは通常NULLとして渡されます
デモは次のとおりです:
USE DBTest2014 GO SET NOCOUNT ON; GO --Set the SIMPLE recovery mode with no auto-stats -- to avoid unwanted log records ALTER DATABASE DBTest2014 SET RECOVERY SIMPLE; ALTER DATABASE DBTest2014 SET AUTO_CREATE_STATISTICS OFF; CREATE TABLE [TEST_TABLE] ([C1] INT, [C2] INT, [C3] INT); INSERT INTO [TEST_TABLE] VALUES (1,1,1); GO --Clear out the log CHECKPOINT; GO -- Implicit transaction INSERT INTO [TEST_TABLE] VALUES (2,2,2); GO SELECT * FROM fn_dblog(null, null); GO
これが短縮された結果セットです。実際、fn_dblogは、ログレコードの長さ、フラグビット、ログ予約、AllocUnitId、PageID、SlotID、前のページのLSNなどのさまざまな異なるレコードを返します。
行の内容の変更
ログの変更は、次の2つの方法でログに記録されます。 LOP_MODIFY_ROW またはLOP_MODIFY_COLUMNS 記録。どの方法を使用しても、実際に変更されているバイトがログに記録されます。たとえば、INT値を1から24に変更すると、他の3つのゼロバイトは変更されなかったため、1バイトの変更のみがログに記録されます。 SQLServerはLOP_MODIFY_ROWを使用します 更新されている行の一部がある場合は、レコードをログに記録します。一部は次のように定義されます。複数の列が更新されている場合でも、行の各可変長列は「部分」であり、行の固定幅領域全体は「部分」です。ただし、バイトが更新されている場合に限ります。更新されるのは、行内で16バイト以下の間隔です。
LOP_MODIFY_ROW 含まれるもの:
- 画像の前
- 残像
- 該当する場合はキー列にインデックスを付けます
- ビットマップをロックする
LOP_MODIFY_COLUMNS 含まれるもの:
- オフセット配列の前後
- 長さ配列
- 該当する場合はキー列にインデックスを付けます
- ビットマップをロックする
- 画像ペアの前後
報酬ログレコード
これは、トランザクションのロールバックを支援するために使用される特別な種類のログレコードです。トランザクションがロールバックするとき、トランザクションの各ログレコードによって記述された変更は、データベースで元に戻す必要があります。ロールバックは、トランザクションの最新のログレコードから始まり、LOP_BEGIN_XACTログレコードまで前のLSNリンクをたどります。ログレコードごと:
- ログレコードの影響を無効にする「アンチオペレーション」を実行します
- ログレコードを生成し、トランザクションの前方部分のログレコードを補正するため、COMPENSATIONログレコードとしてマークします。
- COMPENSATIONログレコードの以前のLSNは、補正対象のログレコードより前のログレコードを指します。これにより、基本的に、ログレコードはトランザクションのログレコードのチェーンの一部ではなくなります。
- ログレコード用に予約されたログスペースが解放されます
補償ログレコードは元に戻すことはできず、やり直すだけです。
トランザクションのロールバック
トランザクションがロールバックしたときに何が起こっているかをグラフで表したものは次のとおりです。
次のコードを調べてみましょう:
USE DBTest2014 GO SET NOCOUNT ON; GO ALTER DATABASE DBTest2014 SET RECOVERY SIMPLE; ALTER DATABASE DBTest2014 SET AUTO_CREATE_STATISTICS OFF; CREATE TABLE [TEST_TABLE] ([C1] INT, [C2] INT, [C3] INT); INSERT INTO [TEST_TABLE] VALUES (1,1,1); INSERT INTO [TEST_TABLE] VALUES (2,2,2); GO --Clear out the log CHECKPOINT; GO -- Explicit transaction to insert a new record BEGIN TRAN; INSERT INTO [TEST_TABLE] VALUES (3,3,3); GO SELECT * FROM fn_dblog(null, null); GO --Roll it back ROLLBACK TRAN; GO SELECT * FROM fn_dblog(null, null);
ここでは、「COMPENSATION」という説明のある特別なログレコードを見ることができます
以前のLSNを見ると、 LOP_INSERT_ROWSであることがわかります。 …0f40:0001にリンクします トランザクションの前方部分が前のログレコードにリンクしているため、これはBEGINトランザクションです。 LOP_DELETE_ROW 補償ログレコードは、補償対象のレコードにリンクしません—(BEGINトランザクションログレコードに)リンクします。
そのため、DELEDEはINSERTを補正し、ログレコードのリストから削除しました。 LOP_ABORT_XACT トランザクションがロールバックで終了したことを示すシグナルです。また、 LOP_ABORT_XACT LOP_BEGIN_XACTに戻るリンク。
補償ログ記録を行うと、ログスペース予約がダウンします[-74]。したがって、実際には、トランザクションの前方部分(LOP_INSERT_ROWS [178])用に予約されていたスペースが返されます。ご覧のとおり、ログスペース予約システムは非常に保守的です。INSERTはDELETEが返すよりも多くのスペースを予約します。
ロールバックと差分バックアップ
データベースで完全バックアップが取られている場合、トランザクションは100.000レコードを更新しますが、トランザクションはロールバックされます。なぜ差分バックアップで大量のデータが使用されるのでしょうか。確かに、トランザクションのロールバックは何も変更されていないことを意味しますか?ここで欠けているパズルのピースは、トランザクションをロールバックしても、トランザクションによって行われたすべての変更が消去されるわけではないということです。これまで見てきたように、ロールバックはトランザクションの前方部分を補正するために他の変更を生成する必要があるため、ロールバックは補正ログレコードを生成する必要があります。影響を受けるすべてのページのページヘッダーが少なくとも2回変更されました。 1つはトランザクションのフォワード部分のページのLSNを更新し、もう1つはトランザクションのロールバック部分のページのLSNを更新します。どちらの場合も、更新により、ディファレンシャルビットマップでエクステントが変更済みとしてマークされます。変更が何であったかは関係ありません。範囲内の何かが変更されただけです。これらのエクステントを差分バックアップから除外する方法はありません。
概要
この記事では、ログレコードを確認しました。ログレコードは、ログと回復のメカニズムの中核です。データベースの各変更には、ログレコードが関連付けられています。各ログレコードには、小さな変更が記述されています。大きな変更では、1つのトランザクション内に複数のログレコードがあります。ログレコードにはさまざまな種類があり、そのうちのいくつかを調べました。
トランザクションログは本質的に巨大なトピックであり、いくつかの記事ではすべての詳細を明らかにするのに十分ではありません。したがって、より詳細な情報が必要な場合は、次の本を読むことをお勧めします。SQLServer Transaction Log Management by Tony Davis and Gail Shaw およびこの記事:トランザクションログ管理。
また読む:
SQL Serverトランザクションログの詳細—パート1
SQL Serverトランザクションログの詳細—パート2