先日は DELETE / TRUNCATE について投稿をしてみました。
SQL Server の DELETE の基本動作を見てみる
SQL Server の TRUNCATE TABLE の基本動作を見てみる
本日は DELETE した際のゴーストレコード (非実体レコード) を削除するゴーストクリーンアップタスク (GhostCleanupTask) について少し見ていきたいと思います。
■事前準備でトレースフラグを設定
ディスクの i/O を少し制御したいのでまずは 2 種類のトレースフラグを設定しておきたいと思います。
- -T661
- ゴーストレコードの削除処理を無効
- -T3505
- 自動チェックポイントを無効
作業負荷の高いパフォーマンスで実行するときのチューニング SQL Server 2005 の SQL Server 2008 のオプション
INF: コントロール SQL Server のチェックポイントの動作をトレース フラグ 3505 を使用します。
ゴーストレコードを削除するときは実データの削除が発生しますのでデータファイルに対して I/O が発生します。
チェックポイントの発生時にもダーティーページの削除が発生しますのでデータファイルに対しての I/O が発生します。
この 2 種類の処理を手動で実行するようにして、ディスク I/O を制御できる状態にしておきます。
以下のクエリを実行して上記のトレースフラグを設定しておきます。
DBCC TRACEON(661, -1) |
[DBCC TRACESTATUS] を実行して、[661] と [3505] が認識されていれば、準備は完了です
■ゴーストレコードについて
DELETE の投稿をした際にも触れましたゴーストレコード (非実体レコード) もう少し踏み込んで見ていきたいと思います。
今回は以下のテーブルで検証をしています。
CREATE TABLE [dbo].[Table_1]( CREATE NONCLUSTERED INDEX [IX_Table_1] ON [dbo].[Table_1] |
クラスタ化インデックスと非クラスタ化インデックスを持つシンプルなテーブルを作成しています。
このテーブルに以下のクエリでレコードを挿入します。
SET NOCOUNT ON |
これでゴーストレコードを確認する準備が整いました。
DELETE を実行する前にクラスタ化インデックスと非クラスタ化インデックスのページ情報を確認しておきます。
# DBCC PAGE のページ番号は私の環境での値ですので、この値は DBCC IND の状態に応じて適宜変更します。
DBCC IND(N’TEST’, N’dbo.Table_1′, 1) DBCC TRACEON(3604) |
[クラスタ化インデックスのスロット 0] Slot 0 Offset 0x6a7 Length 1607 Memory Dump @0x000000001514A6A7 0000000000000000: 10004406 31313534 38423039 2d363039 372d3431 [非クラスタ化インデックスのスロット 0] Slot 0 Offset 0x60 Length 1604 Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 1604 Memory Dump @0x000000001615A060 0000000000000000: 16323134 38424132 452d4633 36342d34 3646422d |
それでは、[DELETE FROM Table_1] を実行して、テーブルのデータを削除し、再度ページ情報を確認します。
[クラスタ化インデックスのスロット 0] Slot 0 Offset 0x6a7 Length 1607 Record Type = GHOST_DATA_RECORD Record Attributes = NULL_BITMAP Record Size = 1607 Memory Dump @0x000000001514A6A7 0000000000000000: 1c004406 31313534 38423039 2d363039 372d3431
[非クラスタ化インデックスのスロット 0] Slot 0 Offset 0x60 Length 1604 Record Type = GHOST_INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 1604 Memory Dump @0x000000001615A060 0000000000000000: 1a323134 38424132 452d4633 36342d34 3646422d |
DELETE を実行することで削除対象となったレコードは非実体レコードとなります。
データ行の場合は [GHOST
_DATA_RECORD]、インデックス行の場合は [GHOST_INDEX_RECORD] となります。
DELETE 前後で特定のバイトが変わっていることが確認できるかと思います。
[クラスタ化インデックスのスロット 0] 前 : 0000000000000000: 10004406 31313534 38423039 2d363039 372d3431 前 : 0000000000000014: 43322d39 4332312d 41333930 32333232 31343432 [非クラスタ化インデックスのスロット 0] 前 : 0000000000000000: 16323134 38424132 452d4633 36342d34 3646422d 前 : 0000000000000014: 42414141 2d343236 36314131 32374435 44202020 |
DELETE では対象となったレコードのステータスビットをゴーストレコード用のものに変更を行います。
ゴーストレコードかどうかはステータスビットの状態によって判断できるということが確認できますね。
ゴーストレコードは以下のクエリでも確認することができます。
SELECT |
こちらは 100 件のレコードが格納されているときの状態です。
クラスタ化インデックスも非クラスタ化インデックスも B-Tree (Balanced Tree) 構造で管理されます。
index_level の数字の低いもの程リーフに近くなります。
index_level = 0 の record_count は両方とも 100 となっているのが確認できますね。
それでは DELETE を実行した後に再度情報を取得してみます。
リーフのレコードがすべてゴーストレコード (ghost_record_count) になっているのが確認できますね。
# 全件を削除してもルートと中間ノードにはゴーストレコードが発生していないのが興味深いですね。
[DBCC FORCEGHOSTCLEANUP] を実行してゴーストクリーンアップタスクを実行するとこのようになります。
各ノードからゴーストレコードが削除されているのが確認できますね。
# 1 レコードがゴーストレコードとして残ってしまう理由がいまいちわかっていないのですよね…。
■ゴーストクリーンアップタスクのディスク I/O を確認してみる
それでは、以下のクエリを実行して DELETE 時のディスク I/O を確認してみます。
# I/O を確認したいので対象となるデータは 10,000 件に増やしています。
DELETE FROM Table_1 |
青がデータファイル / 赤がログファイルに対してのディスク I/O になります。
10 秒間隔でクエリを実行していますので、最初の山から
- DELETE FROM Table_1
- 削除対象レコードをデータファイルから Read
- 削除のログレコードをログファイルに Write
- CHECKPOINT
- メモリ上のダーティーページをデータファイルに Write
- DBCC FORCEGHOSTCLEANUP
- ゴーストレコードクリーンアップタスクのログレコードをログファイルに Write
- CHECKPOINT
- メモリ上のダーティーページをデータファイルに Write
となります。
今度はダーティーページの状態を見てみたいと思います。
DELETE 直後のメモリ上のページの状態を以下のクエリで取得します。
SELECT |
[is_modified = 1] がメモリ上のデータは更新されているがディスク上のデータは更新されていないものになります。
全件 DELETE をした場合、大半のページがダーティーページになっていることが確認できますね。
メモリ上にのみ更新されていたデータがディスクに書き込まれますので、ダーティーページがなくなります。
それではこの状態 (メモリ上にデータがロードされている) で、 [DBCC FORCEGHOSTCLEANUP] を実行してダーティーページを確認してみます。
メモリ上に存在しているゴーストページはダーティーページとなりますので、[is_modified = 1] のページが発生していますね。
それでは CHECKPOINT を実行して再度ページ情報を取得します。
ダーティーページが消えていますのでメモリ上のデータがディスクに書き込まれているのが確認できます。
DELETE してゴーストレコードにした後のチェックポイント / ゴーストレコードを削除するための処理を実行した後のチェックポイントでデータファイルに対して書き込みが発生したのはこのような動きになっているからだと思います。
長々と書いてしまったので今回はここまでで。
次の投稿でもゴーストクリーンアップタスクについて引き続きみていきたいと思います。