今回は [データベースの整合性確認の負荷] について振り返っていきたいと思います。
前回の振り返りでまとめていたデータベースの整合性確認 (DBCC CHECKDB) ですが、データベースの状態を確認するためには大事になる処理ですが、整合性を確認するためには負荷がかかります。
SQL Server の情報をメインに Microsoft 製品の勉強内容を日々投稿
今回は [データベースの整合性確認の負荷] について振り返っていきたいと思います。
前回の振り返りでまとめていたデータベースの整合性確認 (DBCC CHECKDB) ですが、データベースの状態を確認するためには大事になる処理ですが、整合性を確認するためには負荷がかかります。
先日、SQL Server 2005 SP4 が提供されました。
Microsoft SQL Server 2005 Service Pack 4 RTM
Windows Server 2008 のクラスタ環境にインストールした SQL Server 2005 にサービスパックを適用する場合には注意点がありますのでまとめてみたいと思います。
# 2008 以外でも発生するかもしれません。(2003 では発生しなかった気もするのですが)
SQL Server 2005 のメインストリームサポート終了日が [2011/04/12] と半年を切っており最新のサービスパック適用の機会があるかもしれないので軽く書いておこうかと。
今回は SP4 を対象としてまとめていますが、SP1 ~ SP3 , Cumulative Update に関しても同様の手順が必要になります。
# CU に関しては、発生するかは更新内容によって変わると思うのですが。
クラスタ環境で SQL Server 2005 のサービスパックをインストールする場合は、SQL Server のリソースを保持しているノードでサービスパックのインストールを実行します。
SQL Server のリソースを保持していないノードでサービスパックのインストールを実行しても、[MSSQLSERVER] (SQL Server ノーサービスコンポーネント) は選択をすることができません。 ![]()
クラスタ環境の SQL Server 2005 でサービスパックをインストールすると [データベース サービス] のインストールで失敗する場合があります。 ![]()
インストールの失敗ですが以下の技術情報が該当します。
An error message is logged in the Summary.txt file when a SQL Server 2005 service pack, cumulative update or cluster hotfix installation fails: "The Transaction Manager is not available"
SQL Server 2005 のサービスパックのインストールログですが以下のディレクトリに出力されます。
[C:Program FilesMicrosoft SQL Server90Setup BootstrapLOGHotfix]
このディレクトリの中に、[Summary.txt] がありますので、このファイルの内容を確認してみます。
|
Product Installation Status |
データベース サービスで [正常に修正プログラムが適用されたパッシブ ノードはありません] のエラーが出力されています。
このログは [アクティブ ノード] (SQL Server のリソースを持っているノード) のログになりますので、パッシブ ノードでもログを確認してみたいと思います。
# ログはアクティブ ノードと同じディレクトリに出力されています。
|
Product Installation Status |
今回の現象ですが、パッシブ ノードで [データベース サービス] のサービスパック適用に失敗しているのではなく、[クライアント コンポーネント] に対してのサービスパック適用で失敗しています。
この現象ですが、[クラスタの MSDTC のリソースをクライアント コンポーネントの適用が実行されているノードが所有していない] と発生します。
DTC と SQL Server のリソースを [2008-CLS-01] というサーバーで所有させていました。
この状態でサービスパックを適用したため今回の現象が発生していました。
ここからが本題で適用するための手順になります。
[2008-CLS-01] でサービスパックのインストーラーを起動するため、SQL Server のリソースは [2008-CLS-01] に配置しています。
DTC のリソースは [2008-CLS-02] (SQL Server から見てパッシブ となっているノード) に配置しています。
この状態でサービスパックのインストーラーを実行します。
インストールをクリックするとパッシブ ノードのタスク スケジューラに [SqlNodeInstall] といタスクが作成され、パッシブ ノードでサービスパックのインストールが開始されます。 ![]()
このタスクを使用して、インストーラー実行時にパッシブノードの [C:UpdateTemp] にコピーされたサービスパックのプログラムが実行されます。 ![]()
パッシブ ノードのタスクマネージャーを確認すると [msiexec.exe] や [hotfix.exe] が起動されていることが確認できます。 ![]()
インストールを開始してしばらくしているとパッシブ ノードで以下のダイアログが表示されます。
# パッシブ ノードにサービスパックのインストール時に指定したアカウントでログオンしておく必要があります。 ![]()
Windows Server 2008 + SQL Server 2005 の場合、VS 2005 がプログラム互換性アシスタントの警告対象となります。
DTC をパッシブ ノードで実行している場合は、このダイアログが表示できるのですが、アクティブ ノードで DTC を実行している場合はこのダイアログが表示されません。
そのため、クライアント コンポーネントのアップデートができずにエラーとなってしまうようです。
クライアント コンポーネントはアクティブ / パッシブのノード両方でインストールする必要があります。
そのため、アクティブ ノードでクライアントコンポーネントのアップデートが実行される際には、DTC のリソースはアクティブ ノードで実行しておく必要があります。
上記のダイアログがパッシブ ノードで表示されたら、クラスターの管理コンソールを開き、リソースの配置を以下の用に変更しておきます。
# DTC のリソースを [2008-CLS-02] → [2008-CLS-01] に移動しました。 ![]()
DTC の配置が終わったら、[プログラムを実行する] をクリックします。
# アクティブ ノードでクライアントコンポーネントの適用が実行される前までに DTC のリソースを移動しておけば問題ありません。
パッシブ ノードでインストールが終了すると、アクティブ ノードでインストールがされます。
アクティブ ノードでも [クライアント コンポーネント] のインストールが実行されるタイミングで、プログラム互換性アシスタントのダイアログが表示されますので、[プログラムを実行する] をクリックします。
# アクティブ ノードで DTC のリソースが実行されていないと今度はアクティブ ノードでインストールが失敗します。
![]()
この手順でインストールをするとサービスパックの適用が正常に完了します。 ![]()
SQL Server 2005 のサービスパックをクラスタ環境に適用する場合はクラスタの DTC リソースの配置状況に注意する必要があります。
この現象ですが、[クライアント コンポーネント] の更新が入らない場合は発生しないので、CU の適用時にはクライアントコンポーネントが更新対象として含まれていなければ失敗はしないと思います。
今回は [データベースの整合性確認] について振り返っていきたいと思います。
[メンテナンス プラン ウィザード] では、 [データベースの整合性確認] のメンテナンス タスクを作成することが可能です。
このタスクを実行するとデータベースの整合性をチェックすることができます。
セミナーの中では、バックアップの取得前等に実行をすることでデータベースに問題が無いかを確認しバックアップを取得することが可能というようなお話がありました。
今回は、この整合性確認についてまとめていきたいと思います。
まずはデータベースの整合性確認のタスクで実行される内容について確認していきたいと思います。
作成したデータベースの整合性確認のタスクで実行される T-SQL を確認してみます。
![]()
データベースの整合性確認のタスクでは、実行対象として指定したデータベースに対して [DBCC CHECKDB] が実行されます。
# [インデックスを含める] を外すと [NOINDEX] オプションを指定して実行されます。
以下は、BOL の DBCC CHECKDB の記載内容の引用になります。
次の操作を実行し、指定したデータベース内のすべてのオブジェクトの論理的および物理的な整合性をチェックします。
- データベースに対して DBCC CHECKALLOC を実行。
- データベース内にあるすべてのテーブルとビューに対して DBCC CHECKTABLE を実行。
- データベースに対して DBCC CHECKCATALOG を実行。
- データベース内にあるすべてのインデックス付きビューの内容を検証。
- FILESTREAM を使用して varbinary(max) データをファイル システムに格納する場合のテーブルのメタデータとファイル システムのディレクトリおよびファイルの間のリンクレベルの一貫性を検証。
- データベースの Service Broker データを検証。
DBCC CHECKDB では、上記の処理を実行してデータベースの整合性が取れているかを確認する処理になります。
データベースの整合性確認を行う事でどのようなことが分かるか、簡単に試してみたいと思います。
今回は、バイナリエディタでデータファイル (mdf) を開き、一部のデータを書き換え、データの内容とページのチェックサムが一致しないようにしてみました。
データを書き換える前のテーブルの全件を取得した情報がこちらになります。 ![]()
SQL Server 2008 以降であれば、データがどのページに格納されているかは [sys.fn_PhysLocFormatter(%%physloc%%)] を使用することで確認することができます。
# この関数は、Undocument の関数です。
上のデータは、以下のクエリを実行して取得しています。
|
SELECT |
SQL Server のページにはチェックサムがあり、データの内容を元に計算したチェックサムとデータの内容が一致しているかを確認することで、ページが破損しているかを検出する機能がありますのでページに一貫性が無いとエラーとして検出することができます。
# データベースオプションで [CHECKSUM] / [TORN_PAGE_DETECTION] / [NONE] のいずれかを設定することが可能です。([TORN_PAGE_DETECTION] は下位互換となっていますが。)
今回はバイナリエディタでデータベースのファイルを開いて、 (1:114:0) のページを壊してみました。
ページを壊した後に、データを全件取得すると以下のデータまでは取得できるのですが、 (1:114:0) のデータを読み込もうとした際にエラーが発生します。 ![]()
|
メッセージ 824、レベル 24、状態 2、行 1 |
(1:114) という情報が表示されていますので、ファイル ID 1 (mdf ファイル) の 114 ページに一貫性のエラー (チェックサムとデータが不整合) が発生していることが確認できます。
今回、私が壊したページ番号と一致しますね。
破損ページの操作をした場合、イベント ビューアーのアプリケーションにも同様の情報が出力されますので、監視ソフト等でログ監視をしているのであれば、エラーを検知することもできます。 ![]()
今回はデータを全件読み込みしたので破損ページも読み込まれエラーが発生しましたが、破損したページにアクセスをしなければ正常にデータを読み込むことができます。
# Col = 4 のデータを読もうとするとエラーになってしまうので飛ばしています。(先行読み込みの可能性が) ![]()
破損したページを操作しないとエラーにはなりませんので、データベースに異常があるかは分かりません。
DBCC CHECKDB を定期的に実行することで障害により発生した潜在的に潜んでいるデータベースの異常をチェックすることが可能です。
[DBCC CHECKDB(N’TEST’)] を実行して、今回整合性のエラーが発生している [TEST] データベースでデータベースのチェックをしてみます。
|
‘TEST’ の DBCC 結果。 |
DBCC CHECKDB を実行することで、データベースの状態をチェックしてくれますので、破損している領域を操作しなくても異常があるかどうかの確認をしてくれます。
イベントビューアーと SQL Server のログにも結果の概要が出力されますので、後で結果を再確認することも可能です。
![]()
SQL Server のダンプのテキストも出力されています。 ![]()
[msdb..suspect_pages] を SELECT することで直近の情報を取得することもできます。 ![]()
DBCC CHECKDB をバックアップ実行前に実施する理由ですが、[障害が発生しているデータベースのバックアップは障害が発生した状態] となるからです。
障害が発生していない状態のデータベースのバックアップがないと破損している領域を修復することができません。
# SQL Server 2008 以降のデータベースミラーリングであれば、破損ページを読み込んだ際にミラー側から修復することも可能ですが。
そのため、バックアップを取得する前にデータベース自体に異常が無いかを確認して、その後にバックアップを取ることが推奨されます。
セミナーの中で質問 / 回答があったのですが、DBCC CHECKDB によるデータベースの整合性確認はデータベースのサイズが大きいとかなり負荷がかかります。
そのため、バックアップのたびに毎回実行するのが難しいかもしれませんね。
次の投稿では [データベースの整合性確認の負荷] についてまとめてみたいと思います。
今回は [バックアップポリシー] について振り返っていきたいと思います。
# 参加から 2 週間経過したのでテンポよくまとめていかないとだいぶ記憶が薄れてきました…。
セミナーでは以下のような図を元にバックアップポリシーについてのお話しがありました。
上の図は復旧モデルを [完全] または [一括ログ] にした場合のバックアップの基本的な設定になります。
# [単純] 復旧モデルの場合は、ログのバックアップが取れませんので、データベースのバックアップのみになります。
セミナーでは日に一度のデータベースバックアップと、一時間おきのログのバックアップで設定というような例でお話をされていたと思います。
SQL Server のバックアップは大きく分けて 3 種類あります。
# ファイル / ファイルグループでのバックアップも取得ができるのですが、今回は割愛しておきます。
完全 / トランザクションログは上の図で [データベース] [ログ] と書かれているバックアップになります。
[完全バックアップ] は、その時点 (バックアップを実行したタイミング) でのデータまで復元することが可能なバックアップとなります。
完全バックアップはそのバックアップ単体で戻すことが可能ですのでバックアップにはログも含まれています。
[差分バックアップ] は、前回の完全バックアップからの差分データのみを含んだバックアップになります。
完全バックアップを元にした差分になりますので、単体で戻すことはできず必ず前回の完全バックアップが必要となります。
また、差分バックアップは前回の完全バックアップからの差分データですので、完全バックアップを取得してからの期間が長くなり、全データが更新された状態になってしまうと、完全バックアップ相当のバックアップファイルのサイズとなります。
[トランザクションログバックアップ] は、ログレコードのバックアップになります。
復旧モデルのときにもお話しがあったのですが、復旧モデルを [完全] [一括ログ] にしている場合は必須になるバックアップとなります。
これらの復旧モデルに関しては、ログレコードは自動的に切り捨てはされず、バックアップを取得したタイミングで切り捨てが行われ、切り捨てられた個所が再利用可能となります。
そのため、ログのバックアップを取得しないとログがずっと蓄積された状態となり、ログ領域のディスクが枯渇することになります。
ログのバックアップは障害発生時に重要となるバックアップのため、できるだけ短い間隔で取得することが重要であるとセミナーではお話がありました。
# 1 時間おきのログのバックアップというお話はこのタイミングであった気がします。
データベースの完全バックアップを取得していない状態ではログバックアップは取得することはできません。 ![]()
バックアップについてですが、以下の資料がわかりやすいと思います。
SQL Server 2000 の資料ですが、それ以降のバージョンでも考え方は同じですので。
第 4 章 バックアップと復元
定期的なバックアップの設定方法として、[メンテナンスプランウィザード] によるバックアップの設定が紹介されていました。
ワンタイムのバックアップであれば、データベースを右クリックして、[タスク] → [バックアップ] からバックアップを取得することが可能です。 ![]()
このバックアップに関しては、ワンタイムになりますので定期的なバックアップの取得の設定はできません。
定期的なバックアップをウィザード形式で設定する場合は、セミナーで紹介のあった [メンテナンスプランウィザード] を使用する事で設定ができます。
メンテナンスプランウィザードは [管理] → [メンテナンス プラン] → [メンテナンス プラン ウィザード] から起動することができます。 ![]()
メンテナンス プラン ウィザードを使用することで、データベースのメンテナンスをする仕組みを数ステップで設定が可能となります。 ![]()
今回のセミナーでお話しのあったメンテナンスプランに似たものを実際に作ってみたいと思います。
以上でメンテナンスプランの作成は完了です。
今回は、[完全 バックアップ] の取得と、[トランザクションログ] のバックアップの取得を設定していますので二つのジョブが SQL Server エージェントに作成されています。
作成した 2 つのジョブを手動で実行して、バックアップの取得状況を確認してみたいと思います。
ジョブを手動実行する場合は、対象のジョブを右クリックして、[ステップでジョブを開始] をクリックします。 ![]()
今回は、H ドライブにアックアップを取得しているのですが、データベースのバックアップ (.bak) とトランザクションログのバックアップ (.trn) が取得されていることが確認できます。 ![]()
今回のメンテナンスプランですが、バックアップを取得するたびにタイムスタンプが設定されたバックアップファイルが蓄積されていきます。 ![]()
また、ログに関してもバックアップを実行するたびに蓄積されていきます。 ![]()
このバックアップとログの履歴 (世代) 管理ですが、[メンテナンス クリーンアップ タスク] を使用することで管理することができます。
先ほど作成したメンテナンスプランにこの履歴クリーンアップタスクを組み込んでみたいと思います。
作成したメンテナンスプランは、[管理] → [メンテナンス プラン] の下に作成がされますので、作成したプランをダブルクリックします。 ![]()
そうするとメンテナンスプランのデザインウィンドウが開きます。 ![]()
先ほど作成したプランが、[Subplan_1] [Subplan_2] として設定されています。
今回は [サブプランの追加] をクリックして、[Subplan_3] として[メンテナンス クリーンアップ タスク] を設定します。 ![]()
タスクをダブルクリックすると設定画面になります。
このタスクを使用することで、バックアップファイルとメンテナンスプランのレポートを削除することが可能です。
拡張子単位で指定はできるのですが、ワイルドカードは使えないので、[bak] [tran] 用のバックアップファイル削除タスクを用意します。 ![]()
![]()
テキストレポートのクリーンアップタスクとしては以下の内容を設定しました。 ![]()
今回はスケジュールも設定し、このような形でサブプランを設定してみました。 ![]()
今回は 4 週経過したファイルが対象になっていますので、以下の PowerShell を実行して、ファイルの更新日付を変更してみました。
# クリーンアップタスクでファイル操作をする際の日付はファイルの更新日付が検索対象となります。
| Get-Item "C:Program FilesMicrosoft SQL ServerMSSQL10_50.SQL2008R2MSSQLLogMaintenancePlan_Subplan_1_20101220221815.txt" | Set-ItemProperty -name LastWriteTime -Value (Get-Date).AddDays(-28) |
これで、メンテナンスレポートの日付が 28 日前 (4 週間前) になりました。 ![]()
それでは、作成したクリーンアップタスクんのジョブを実行してみます。
ジョブを実行したことで、先ほどファイルの更新日付を変更したメンテナンスレポートが削除されていることが確認できます。
バックアップ ファイルに関しても更新日付が条件に一致するのであれば削除されます。
![]()
メンテナンス プラン ウィザードを使用することでバックアップのメンテナンスプランを GUI のウィザードベースで柔軟に設定することが可能となります。
今回の投稿では設定をしなかったのですが、メンテナンスプランには [データベースの整合性確認] というメンテナンス タスクがあります。
セミナーの中ではこのタスクについてもお話がありました。
次の投稿では、データベースの整合性確認についてまとめてみたいと思います。
SQL Server のインストールですが、インストール時に [インスタンス ルート ディレクトリ] を設定します。 ![]()
インスタンス ルート ディレクトリは任意のディレクトリを指定できますので、デフォルトの [C:Program FilesMicrosoft SQL Server] から変更することが可能です。
このインスタンス ルート ディレクトリですが、変更した際に一点注意点があります。
今回は、C ドライブではなく E ドライブに設定をしてみました。 ![]()
インスタンス ルート ディレクトリ以外は C ドライブに設定しています。 ![]()
SQL Server をインストールすると以下のサービスがインストールされます。
インスタンス ルート ディレクトリに選択したディレクトリには、[SQL Server] [SQL Server Agent] のサービスのファイルが格納されます。
![]()
サービス用のプログラムが C ドライブ以外にあることは問題ないのですが、この状態では Windows Server バックアップで [システム状態] [ベアメタル 回復] のバックアップを取得する際に、SQL Server 用のサービスに必要なプログラムが格納されているドライブも取得対象となります。
# このあたりは、System Writer の VSS Writer が制御しているみたいですね。レジストリ (ImagePath の値) を直接修正して、サービスで使用するプログラムの場所を変更すればインスタンス ルート ディレクトリを取らなくすることもできますが。危険すぎてこの方法は使わないですよね…。
最初は [ベア メタル回復] のバックアップ取得を試してみます。 ![]()
ベア メタル回復を選択すると、自動的にベアメタル回復に必要なドライブが選択されるのですが、C ドライブだけでなく E ドライブも自動的に選択がされます。
E ドライブを外そうとしても以下のメッセージが表示され、ドライブ / ドライブ以下のフォルダを外すことができません。 ![]()
[システム状態] を選択すると、システム状態の回復に必要なものが取得されます。
# ドライブの選択状況は変わらず、内部でシステム状態の回復に必要なものが自動で取得されます。 ![]()
システム状態を選択した際に取得されるバックアップを確認してみます。
3 つの VHD ファイルが作成されています。 ![]()
これですが、[システムで予約済み] (通常にインストールをした際の先頭 100MB) [C ドライブ] [E ドライブ] のバックアップとなっています。
Windows Server 2008 R2 の場合はファイル単位でバックアップが取得できるようになているので、各ドライブのバックアップはシステム状態として必要最低限のファイルがバックアップされています。
# C ドライブであればユーザープロファイルのディレクトリは除外されています。
E ドライブのバックアップを確認してみるとサービスで使用している 2 つのファイルのみが取得されています。 ![]()
Windows Server 2008 R2 であれば、ファイル単位で取れるのでシステム状態を取得した時にインスタンス ルート ディレクトリを C ドライブ以外にしても最小限のファイルがバックアップされることになります。
Windows Server 2008 ではどうなるでしょう。
Win
dows Server 2008 では[システム回復を有効にする] がベアメタル回復相当のバックアップとなります。
このオプションを有効にすると、インスタンス ルート ディレクトリに指定したドライブも自動的に選択されます。 ![]()
この動きは、Windows Server 2008 R2 と同じですね。
それでは、システム状態のバックアップを取得するとどうなるでしょう。
Windows Server 2008 の場合は、[wbadmin start systemstate
backup] というコマンドラインを使用して、システム状態だけを取得することが可能です。
# [Start Backup] で取得する場合は、[-allCritical] というオプションを指定します。
システム状態を取得する場合に必要となるファイルが含まれているドライブを自動で判断する動きは Windows Server 2008 R2 と変わりません。 ![]()
Windows Server 2008 の場合は、デフォルトでは先頭の 100MB の領域は作成されないので、2 つの VHD ファイルが取得されます。 ![]()
Windows Server 2008 R2 と異なり、サービス以外のファイルも取得されています。
Windows Server 2008 の場合は、ボリューム単位でのバックアップしか取得ができないため、SQL Server のサービスで使用しているファイル以外のものも取得がされます。
そのため、インスタンス ルート ディレクトリのドライブに SQL Server のデータベースを格納していると、Windows Server 2008 ではシステム状態のバックアップでデータベースのファイルも取得がされてしまい、バックアップのファイルサイズが肥大化する可能性があります。
インスタンス ルート ディレクトリを変更することはあまりないかもしれませんが、変更する場合はバックアップの取得で影響がないようにデータベースのファイルを配置するような考慮が必要そうですね。
先日、SQL Server の WMI イベント警告について投稿をしました。
WMI イベント警告は SQL Server Agent 経由で実行されるため、SQL Server の権限も SQL Server Agent のサービスアカウントのものになります。
BOL に記載されているのですが、WMI イベント警告で警告を受信するためには、以下の権限が必要となります。
WMI 警告の通知を受信するには、SQL Server エージェントのサービス アカウントに、WMI イベントを含む名前空間、および ALTER ANY EVENT NOTIFICATION に対する権限が許可されている必要があります。
SQL Server Agent をローカルシステムアカウント (LocalSystem : NT AUTHORITYSYSTEM) で実行した場合は、特に設定を変更しなくても WMI イベント警告を受信できると思います。
SQL Server Agent を一般ユーザー (Users グループのユーザー) で起動した場合はどうなるでしょう。
イベントの条件に達してもイベントビューアーのアプリケーションに [124] のエラーが出力されて WMI イベント警告が実行されません。
![]()
![]()
この挙動の原因ですが、ローカルシステムアカウントの場合は、[ALTER ANY EVENT NOTIFICATION] のサーバーレベルの権限が付与されています。
サーバーレベルの権限は以下のクエリで確認ができます。
| EXECUTE? AS LOGIN = ‘NT AUTHORITYSYSTEM’ SELECT SUSER_NAME(), USER_NAME() SELECT * from fn_my_permissions(NULL, ‘SERVER’) ORDER BY permission_name REVERT |
SQL Server Agent が SQL Server に接続する場合は、[NT SERVICESQLAgent$<インスタンス名>] のログインで実行がされます。
# OS によってはサービスアカウントがそのまま使用されていることもあります。
![]()
このログインは、Windows 認証になっているので、Windows アカウントを使用したログインとなっています。
Windwos? Server 2008 R2 の場合はこのアカウントはサービス SID になっていて、Windows のログインやグループではなく、サービスに直接関連づいたアカウントになっています。
サービス SID の場合は、上記のクエリだと権限が見えないのですよね…。
サービス SID を使用していない OS (XP / 2003)? の場合は、SQL Server Agent の起動アカウントに、[ALTER ANY EVENT NOTIFICATION] の権限は付与されていませんでした。
ローカルシステム以外で SQL Server Agent を起動しているときは、以下のクエリを実行して、権限を付与する必要があります。
| USE [master] GO GRANT ALTER ANY EVENT NOTIFICATION TO [NT SERVICESQLAgent$SQL2008R2] |
権限を外す場合はこちらになります。
# REVOKE で権限を外します。
| USE [master] GO REVOKE ALTER ANY EVENT NOTIFICATION TO [NT SERVICESQLAgent$SQL2008R2] |
ALTER ANY EVENT NOTIFICATION の権限を付与することで、WMI イベントを SQL Server Agent のサービス起動ユーザーで受信できるようになり、WMI イベント警告を実行できるようになります。
WMI イベントを含む名前空間に関しては、インストール直後の状態の権限で問題は無いようです。
WMI の権限の確認についてですが、ファイル名を指定して実行から MMC を起動して、[WMI コントロール] を追加します。![]()
MMC に WMI のスナップインを追加した後に、WMI コントロールのプロパティを開きます。
![]()
WMI コントロールのプロパティが開いたら、[セキュリティ] タブの [RootMicrosoftSqlServerServerEvents<インスタンス名>] を選択して、[セキュリティ] をクリックします。
![]()
そうすると、WMI 名前空間に対してのセキュリティを確認することができます。
![]()
SQL Server Agent のサービス SID には、[メソッドの実行] [アカウントの有効化] [セキュリティの読み取り] の権限が付与されています。
WMI イベント警告を実行するために必要な権限はこの権限でよいようで、デフォルトから設定は変更しなくても実行することができました。
サーバーレベルで権限を付与する必要があり、この操作は普段あまりやらない作業だと思いますので、ちょっと戸惑うかもしれないですね。
今回は [パフォーマンス条件警告] について振り返ってみたいと思います。
セミナーの中で SQL Server のパフォーマンス条件警告をしようして、ログの使用状況が一定の閾地に達した場合にログのバックアップを取得して、ログを切り捨てるという方法が紹介されました。
それでは、パフォーマンス条件警告についてまとめていきたいと思います。
パフォーマンス条件警告ですが、[SQL Server Agent] を使用して動作します。 ![]()
設定は
という流れになります。
# 実際の設定は順不同でジョブと通知方法は必須ではないですが。
それでは、実際にパフォーマンス条件警告を設定していきたいと思います。
今回はセミナーでお話しのあったものと同じ設定を行っていきます。
今回は通知設定はしていませんが、通知は以下の方法で行う事ができます。 ![]()
それでは実際にデータを追加して挙動がどのように変わるかを試してみたいと思います。
今回も自動チェックポイントは無効にしています。
パフォーマンス条件警告を無効にしている場合の状態はこのようになります。
# 作成した警告は、個別に無効化することができます。 ![]()
ログの書き込み待ちとバックアップ処理時間は 2 軸にしていますが、これらは発生していないことが確認できます。
それでは、パフォーマンス条件警告を有効にして同じ処理を実行してみます。
こちらも、バックアップ処理時間は 2 軸に表示しています。
ログの書き込み待ちに関しては、カウンタがあがっていることを示すために、元の値に 10,000 をかけています。
パフォーマンス条件警告の設定内容が有効になっていますので、ログファイルの使用状況が一定に達したタイミングで自動的にログのバックアップが行われています。(バックアップ処理時間はデータ/ログのバックアップを取得すると発生します。)
今回作成したパフォーマンス条件警告のサンプルスクリプトがこちらになります。
|
USE [msdb] /****** Object: Job [TEST Database Log Backup] Script Date: 12/16/2010 21:09:09 ******/ END DECLARE @jobId BINARY(16) GO
EXEC msdb.dbo.sp_add_alert @name=N’TEST Database Log Auto Backup’, |
このスクリプトですが、[INIT] を指定してログのバックアップを取得しています。
本来の運用では、この指定は NG です。
# テスト用でログのバックアップを蓄積したくなかったので INIT でバックアップを上書きしています。
ログのバックアップはログチェーンという考えがあり、連続したログのバックアップが担保されている必要があります。
ログチェーンが途切れてしまうとログを使用したリストアができませんので、本番運用で使う場合は [NOINIT] かログファイルの名称を動的に生成するようにしてログを上書きしないようにする必要があります。
セミナー中に Twitter で以下のような質問がありました。
「オンライン中のトランザクションログバックアップのパフォーマンス劣化はどれくらいなのでしょうか?」
ログのバックアップをするとログの書き込み待ちが発生しているのが上のグラフで確認することができます。
# 今回の環境では 200 MB 程度のログのバックアップを書き込む際には、書き込み待ちは最大で 7ms 程度になっていました。
グラフではなくWRITELOG の待ち事象でも比較をし手みたいと思います。
パフォーマンス条件警告を設定していない場合はこのような待ち事象になります。
waiting_tasks_count = 619,844 ? 519,766 = 100,078
wait_time_ms = 459,265 ? 396,561 = 62,704
パフォーマンス条件警告を設定した場合はこのようになります。
waiting_tasks_count = 720,408 ? 619,881 = 100,527
wait_time_ms = 532,001 ? 459,405 = 72,596
ログのバックアップをしている最中はログの使用状況がなだらかになりますのでログの書き込みにも多少の影響が発生します。
また、BOL に以下のように書かれているようにバックアップの実行を行うと CHECKPOINT が発生します。
チェックポイントは次の状況で作成されます。
- CHECKPOINT ステートメントが明示的に実行された場合。接続を確立するために、現在のデータベースでチェックポイントが作成されます。
- データベースで最小ログ記録操作が実行された場合。たとえば、一括ログ復旧モデルを使用しているデータベースで一括コピー操作が実行された場合です。
- ALTER DATABASE を使用して、データベース ファイルが追加または削除された場合。
- SHUTDOWN ステートメント、または SQL Server (MSSQLSERVER) サービスを停止することによって、SQL Server のインスタンスが停止された場合。どちらの場合でも、SQL Server のインスタンスの各データベースでチェックポイントが作成されます。
- データベースの復旧にかかる時間を短縮するために、SQL Server のインスタンスにより、各データベースで定期的に自動チェックポイントが作成されている場合。
- データベースのバックアップが作成された場合。
- データベースのシャットダウンが必要な動作が実行された場合。たとえば、AUTO_CLOSE が ON に設定されていて、データベースへの最後のユーザー接続が終了した場合、またはデータベースの再起動が必要なデータベース オプションが変更された場合です。
データベースのバックアップにはログのバックアップも含まれています。
先ほどのグラフにチェックポイントの発生状況も追加してみます。 ![]()
バックアップのタイミングに合わせてチェックポイントが発生していることが確認できます。
チェックポイントが発生するとメモリ上のダーティーページをデータファイルに書きだしますので、データファイルに対しても負荷がかかることになります。
この辺はディスクの性能にも依存するのでどれくらいというのは難しいところなのですが、ログの書き込み待ち + チェックポイントによるデータファイルへの書き出しを劣化分として見込んでおく必要があると思います。
# この辺はディスク性能とドライブ構成に依存するところがあるのですよね…。
これは警告のちょっとした応用なのですが、パフォーマンス条件警告ではなく、[WMI イベント警告] を使用することでイベントを受信してジョブを実行することが可能となります。
例えば、ロックエスカレーションが発生した場合にそのイベントを受信してテーブルにロックエスカレーションが発生した時に実行されたクエリを保存してくれるような警告が作れると運用で便利そうですよね。
WMI イベント警告を使用すると、イベントを受信して特定のジョブを実行するということができるようになります。
# イベントを取得してログに出すだけであれば SQL トレースでもできるのですけどね。
BOL には [サンプル : WMI Provider for Server Events の使用による SQL Server エージェント警告の作成] という形でデッドロックをトラップしてテーブルにデータを書きだすサンプルが紹介されています。
# サンプル : WMI Provider for Server Events の使用による SQL Server エージェント警告の作成
ただし、内容に一か所誤りがあり、
| @command= N’INSERT INTO DeadlockEvents (AlertTime, DeadlockGraph) VALUES (getdate(), N”$(ESCAPE_SQUOTE(WMI(TextData))))’ |
を
| @command= N’INSERT INTO DeadlockEvents (AlertTime, DeadlockGraph) VALUES (getdate(), N”$(ESCAPE_SQUOTE(WMI(TextData)))”)’ |
にしないと動かなかったはずですが。
# シングルクォート × 2 が足りていないのですよね…。
WMI イベント警告を使用するための事前準備として 2 つの作業があります。
| SQL Server エージェントが WMI イベントを受信するには、msdb および AdventureWorks2008R2 で Service Broker が有効化されている必要があります。 |
msdb に関してはデフォルトで Service Broker が有効になっているはずなのですが、ユーザーデータベースではデフォルトでは無効になっています。
Service Broker を有効にするためにはデータベースのプロパティを開いて、[Broker が有効] を [True] にする必要があります。
ただし、この変更をする際にはデータベースに対して [LCK_M_X] (排他ロック) を取得しにいきますので、対象のデータベースに接続中のユーザーがいると変更できません。
今回は、SQL Server のサービスを再起動して、サクッと全ロックを外してから実行してしまいました。
クエリで設定を変更する場合は以下のクエリを実行します。
# DB 名は適宜変更する必要があります。
|
USE [master] |
ロックエスカレーション時のクエリは SQL Server 2008 からでないと取得できなかった気もします…。
トークンの置き換えはSQL Server エージェントのプロパティから [警告システム] を選択して、[警告に応答するすべてのジョブのトークンを置き換える] を有効にします。
クエリで有効にする場合は、以下のような内容になります。
|
USE [msdb] |
以上で、事前準備は完了です。
基本的な流れはパフォーマンス条件警告と変わりません。
WMI イベント警告特有な内容としては、WMI の名前空間を指定するのとクエリを WQL ステートメントで記載する箇所になります。
名前空間に関しては通常の WMI の名前空間を指定すれば問題ありません。
SQL Server のサーバーイベントに関しては以下の指定をします。
| \.rootMicrosoftSqlServerServerEvents<インスタンス名> |
私の環境ではこのような指定ですね。
[\.rootMicrosoftSqlServerServerEventsSQL2008R2]
WQL ステートメントを記載するためには、イベント名を記載しなくてはいけないのですが、イベント名は [wbemtest.exe] (Windows Management Instrumentation テスト) を使用すると簡単に確認することができます。
# Windows Server 2008 R2 だと標準で入っています。 ![]()
[接続] をクリックして、SQL Server の名前空間に接続をします。 ![]()
名前空間に接続をしたら、[クラスの列挙] をクリックします。
[再帰] 選択して [OK] をクリックするとクラスの一覧が取得できます。 ![]()
クラスとして表示されたものが WMI イベント警告で受信できるイベントになります。
今回はロックエスカレーションを受信したいので、[LOCK_ESCALATION] というイベントを受信することになります。
LOCK_ESCALATION をダブルクリックするとプロパティを取得することができます。
このプロパティ名がトークンの置き換えで使用できる WMI のプロパティ名になります。 ![]()
続いて応答で使用するジョブを作成します。
今回は発生時刻 / データベース名 / 実行していたクエリの情報をテーブルに格納したいと思います。
そのため、以下のテーブルを事前に作成しておきます。
# 今回は [TEST] というデータベース上に作成しています。
|
CREATE TABLE dbo.LockEscalationEvent |
コマンドにはトークンの置き換えを使用して、WMI のプロパティを ESCAPE_SQUOTE を使って、プロパティ内にシングルクォートが存在している場合は修飾するように指定をしたクエリを設定しています。
以上で、WMI イベント警告の設定は完了です。
それでは実際にロックエスカレーションを発生させて正常に動作するかを見ていきたいと思います。
UPDLOCK を設定して、トランザクション内で大量のデータを SELECT したためロックエスカレーションが発生しています。
そのあとに、JobStep が実行されていますね。
# JobStep は SQL Server Agent サービスの起動アカウントで SQL Server に接続して実行されています。
今回は LocalSystem アカウントで実行しています。
最後の JobStep を確認すると、INSERT 文が実行されていることが確認できます。 ![]()
INSERT 文ですが、トークンの置き換えにより WMI のプロパティが文字列に置き換えられて実行されているのが確認できますね。
作成したテーブルを確認してみます。 ![]()
イベントが発生した日と対象のデータベース、クエリがテーブルに格納されています。
WMI イベント警告を使用することで特定のイベント受信時にジョブを動かくすことができますのでうまく使うと運用が楽になるかもしれないですね。
今回作成した警告とジョブのサンプルスクリプトは以下になります。
|
USE [msdb] /****** Object: Job [JOB LockEscalation] Script Date: 12/16/2010 23:10:33 ******/ END DECLARE @jobId BINARY(16) GO
USE [msdb] /****** Object: Alert [WMI Lock Escalation] Script Date: 12/16/2010 23:10:16 ******/ |
パフォーマンス条件警告と WMI イベント警告を使用することでデータベースの特定の状態を受信してジョブを実行することが可能になります。
# WMI イベント警告は良い機会だったのでついでにまとめたものですが。
パフォーマンス条件警告でログのバックアップを取るのは今まで考えていなかったので、なるほどと思いました。
この後には [バックアップポリシーの検討] ということで、バックアップタクトの一例や障害発生時の復元の概要についてのお話がありました。
次の投稿では、バックアップポリシーについてまとめてみたいと思います。
今回は [自動拡張] について振り返ってみたいと思います。。
セミナーのログ管理のセッションでは [自動拡張] についてのお話がありました。
SQL Server のデータファイル / ログファイルには [自動拡張] という設定があります。
自動拡張を設定することでファイルの空きが無くなったときにディスクに空きがある場合、自動的に拡張するように設定することができます。 ![]()
自動拡張のサイズは、[現在のサイズに対しての比率 (%)] と [MB 単位] で、最大サイズに関しては [MB 単位] と [無制限] で指定することができます。
それでは、ログファイルの自動拡張についてまとめていきたいと思います。
ログファイルは以下の構成となっています。
自動拡張が発生すると以下のように拡張がされます。
# 手動拡張でも同様です。 ![]()
拡張が発生すると拡張分のディスクサイズが確保されます、その拡張分が仮想ログ (VLF) で分割されます。
上の絵では、VLF が 4 つで構成された形になっていますが、拡張するサイズによって 4 / 8 / 16 のどの個数で分割するかが決まります。
そのため、細かな拡張を繰り返すとログファイルを構成する VLF の個数が多くなりパフォーマンスに影響が出てきます。
# これに関しては別の機会にまとめようと思っています。
それでは、実際の環境で自動拡張を見ていきたいと思います。
現在、ログは 13,041,644 Byte の 2 個の VLF で構成がされています。
# VLF は作成 / 拡張時は最小で 4 個になるのですが、ファイルの圧縮 (SHRINK FILE) をすることで最小で 2 個にできます。 ![]()
データベースのプロパティからみた設定はこのようになっています。 ![]()
それでは、データを追加 (INSERT) して、ログファイルの自動拡張を見ていきたいと思います。 ![]()
このグラフはパフォーマンスモニタで、[Log File(s) Used (KB)] と、[Log Growths] を取得したものになります。
# Log Growths に関しては SQL Server のサービスが最後に起動してからの値だったはずなので、0 からは始まっていません。
25MB だったものが、925MB になっています。
今回は、100MB 単位で自動拡張をするように設定していますので、 9 回の拡張が発生したことになります。
上のグラフでも自動拡張の発生回数は 4 → 13 になっていますので回数は合っていますね。
この下にもデータは続いており、全部で 74 個の仮想ログがあります。
100MB の拡張だと一度の拡張に対して仮想ログが 8 個作成されます。
そのため
8 個 × 拡張 9 回 = 72 個 + 初期の 2 個 = 74 個
の仮想ログが作成されたことになります。
自動拡張に関しては、SQL Server のデフォルト トレースにも出力がされます。
デフォルト トレースは SQL Server をインストールしたインスタンスのディレクトリの [LOG] フォルダに出力がされます。
私が今回使用している環境では以下のディレクトリになります。
[C:Program FilesMicrosoft SQL ServerMSSQL10_50.SQL2008R2MSSQLLog]
デフォルト トレースは SQLTrace の .trc ファイルですので [SQL Server Profiler] で開くか [fn_trace_gettable ] を使用してクエリで情報を取得することができます。
今回はfn_trace_gettable を使用して情報を取得したいと思います。
|
DECLARE @filename varchar(500) |
今回は、20:35 ~ 20:37 に実行していますので、デフォルト トレースにもこの時間帯のログは 9 件出力されています。
ログファイルの上限まで達したら拡張ができないため、上限に達した後の追加処理はエラーとなります。
自動拡張を設定しておくことで、当初想定していたログファイルでは足りなくなったときでも処理を継続することができるようになります。
セミナーではログの拡張時には [ログの書き込み待ち] が発生するため、拡張は発生させないように最初から適切なサイズを設定するというお話がありました。
続いてログの書き込み待ちについてみていきたいと思います。
自動拡張だけに限らずログの拡張が行われている際にはログの書き込みがブロックされ、拡張が終わるまで待ち状態となります。
[自動拡張のログファイルの使用状況] のグラフで拡張が発生している際に、ログファイルの使用状況が一瞬止まって入るのが確認できます。
# さほど負荷をかけていない検証なので、本当に一瞬ですが。
自動拡張ではログの書き込み待ちを確認するのが難しいので、データの追加を行っている最中に、10GB のファイル拡張を行って検証をしてみたいと思います。
# 自動チェックポイントの実行は無効にしています。
拡張をしないでデータの追加を行った場合は以下のようなグラフになります。
ログファイルの使用状況も停止することなく上がっており、ログの書き込み待ちに関しては発生していません。
データの追加中に10GB のログの拡張を行ったものがこちらになります。
ログファイルの使用状況が平らになっている部分が拡張を実行したタイミングになります。
ログの拡張中は使用状況の上がりも鈍り、書き込み待ちの秒数が発生しているのが確認できます。
それでは、この書き込み待ちを [WRITELOG] の待ち事象からも見てみたいと思います。
WRITELOG の待ち事象を取得するには以下のクエリを実行します。
| SELECT * FROM sys.dm_os_wait_stats WHERE wait_type = ‘WRITELOG’ |
こちらが通常の追加を行う前後の待ち事象の状態になります。
処理を行っている間に
waiting_tasks_count = 501,013 ? 400,927 = 100,086
wait_time_ms = 306,407 ? 248,635 = 57,772
の WRITELOG に関しての待ち事象が発生していたのが確認できます。
それでは、拡張をした場合はどうなるでしょう。
waiting_tasks_count = 607,419 ? 507,039 = 100,380
wait_time_ms = 378,033 ? 310,391 = 67,642
というようにログの書き込み待ちによる待ち事象が増加していることが確認できます。
余談ですが、ファイルの瞬時初期化 (SQL Server のサービス起動アカウントに [ボリュームの保守タスクを実行] を付与) に関しては BOL に [ログ ファイルの初期化ではそのまま空にする必要がありますが~] と書かれているように有効にしても効果は出ません。
waiting_tasks_count = 100,397 ? 13 = 100384
wait_time_ms = 67,068 ? 86 = 66,982
ログ管理に置いて自動拡張は保険として考え、運用中は発生しないようにすることでログの書き込み待ちを減らし、パフォーマンスの向上につなげることが可能となります。
セミナーでは、ログ管理のもしもの時の備えとして、[パフォーマンス条件警告] を使用したログの管理のお話がありました。
次の投稿では、パフォーマンス条件警告とその簡単な応用についてまとめてみたいと思います。
ログ管理の中でお話頂いた [復旧モデル] について振り返ってみます。
今回は復旧モデルによる、ログの使用状況の違いをまとめて見たいと思います。
SQL Server には復旧モデルという考えがあり、この復旧モデルがリストア時に復旧可能なタイミングとトランザクションログの管理 (解放のタイミング) に影響します。
復旧モデルには、以下の 3 種類があります。
完全復旧モデルと一括ログ復旧モデルに関しては、定期的なバックアップによるログの管理が必要となり、単純復旧モデルに関しては特定のタイミングで自動的に解放 (チェックポイント時のログの切り捨て) が行われます。
そのため、完全 / 一括ログ復旧モデルでないとログを使用したリストアを実施することはできません。
まずは、完全復旧モデルと単純復旧モデルのログの利用のされ方について確認してみたいと思います。
今回は、ログファイルを 30MB にして自動拡張を無効にした状態で大量のデータを追加 (INSERT) してテストをしています。
完全復旧モデルの場合には、チェックポイントが発生してもログは切り捨てられません。そのため、トランザクションログの使用状況が上限に達した際には、トランザクションログが上限に達したというメッセージが表示されそれ以上のデータの追加ができなくなります。
# 一括ログ復旧モデルもチェックポイント時のログ切り捨ては行われないので、同じトレンドになります。
|
メッセージ 9002、レベル 17、状態 2、行 2 |
[sys.databases] を確認すると以下の情報を取得することができます。
今回の場合、ログの領域が再利用できない理由はログ領域がフルになってしまい、バックアップの必要があるという事を確認できます。
それでは、単純復旧モデルにした場合にはログの使用状況はどうなるでしょう。 ![]()
データの追加操作としては全く同じ内容を実行したのですが、単純復旧モデルの場合はチェックポイントの発生タイミングに合わせてログの使用状況が下がっていることが確認できます。
この挙動を [チェックポイント発生時のログの切り捨て] と呼びます。
復旧モデルが単純復旧モデルとなっているデータベースはログのバックアップを取得することができません。
そのため、チェックポイントが発生してメモリ上のダーティーページをディスクに書き込んだタイミングでそれまでのログレコードが不要 (変更箇所がデータファイルに書き込まれているため) となり切り捨てることが可能になります。
チェックポイントの発生ですが、バックアップの取得 / 手動による [CHECKPOINT] ステートメントの実行 / [復旧間隔] の設定に応じた自動チェックポイントなどで発生がします。
今回発生しているチェックポイントは自動チェックポイントにより実行されています。
# 最後の一回だけは私が手動でチェックポイントを発生させているので山の形が少し変わっています。
余談ではありますが自動チェックポイントはトレースフラグ [3505] を設定することで無効にすることもできます。
INF: Use Trace Flag 3505 to Control SQL Server Checkpoint Behavior
それでは、次に完全復旧モデルと一括ログ復旧モデルの違いを確認してみたいと思います。
この復旧モデルの違いですが、最小ログ記録が可能な操作をした際のログの書き込みが変わってきます。
完全復旧モデルでは、すべての操作のログが完全に記録されますが、一括ログ復旧モデルでは最初ログ記録が可能な操作をした場合は、ログは最小限のもののみ記録がされます。
最小ログ記録が可能な操作
# インデックス系の操作は一括ログ復旧モデルでないと、最小ログにならないと思っていたのですが上記の内容を読むとそうでもないらしいですね。別の機会で検証したいと思います。また、操作方法によっては完全復旧にしていても最小ログになってしまったので今回は完全復旧モデルでは意図的に最小ログ記録にならない操作をしています。
今回は、[BULK INSERT] を使って違いを確認してみたいと思います。
![]()
どちらの復旧モデルでも波形は一緒になってしまっていますね。
これですが、今回は以下のようなクエリを実行していました。
| BULK INSERT Table_1 FROM ‘F:Dataexport.txt’ |
BULK INSERT を実行する場合、上記のクエリでは最小ログ記録での動作にはなりません。
そのため、一括ログ復旧モデルでも完全なログの記憶動作となり、両復旧モデルで同じ波形となっています。
BULK INSERT に限った話ではないのですが、一括インポート時のログを最小ログ記録にするためには条件があり、[テーブルロックを指定] する必要があります。
一括インポートで最小ログ記録を行うための前提条件
# 他にも条件はあるのですが、今回はインデックスを持たない 1 列の単純なヒープ構造のテーブルを使用しています。
それでは、クエリを以下のように書き換えて実行をしてみます。
| BULK INSERT Table_1 FROM ‘F:Dataexport.txt’ WITH (TABLOCK) |
波形だけをみると一緒の波形になっているのですが、ログファイルの使用状況には大きな差が出ています。
テーブルロックを使用する前はログファイルの使用状況は 30MB まで上昇していました。
テーブルロックを使用した場合は、20KB 程度の使用で処理を完了することができています。
# 実は、テーブルロックを使用しなかった場合は、ログがフルになっていたりもしたのですが。
それでは、実際のログレコードから完全ログ記録と最小ログ記録の違いを比較してみたいと思います。
完全ログ記録の場合は [LOP_INSERT_ROWS] という形で、追加した行単位でログレコードを書き込んでいきます。
最小ログ記録の場合は、[LOP_SET_BITS] という形で行を追加するにあたって [変更のあったエクステント] の情報のみを書き込んでいます。
最小ログ記録が発生する、一括ログ操作に関しての変更は BCM (一括変更マップ : Bulk Changed Map) という領域を使用して管理されるためこのような動きになります。
使用する復旧モデルによって、ログの使用状況に差が出てきます。
単純復旧モデルを使用した場合はチェックポイント発生時に解放できるログは解放されますが、それ以外の復旧モデルに関してはログのバックアップを取得ないと使用している領域は解放されません。
ログはログのバックアップのタイミングを考慮したうえで、そのタイミングで解放される領域で処理がうまく回るようなサイズを指定しておくことで、ログがフルにならず処理を継続することが可能となります。
ただし、一時的なトランザクションの増加に備えて、[自動拡張] を設定して保険とすることも考えられれます。
セミナーの中では自動拡張時のログの書き込み待ちについても触れられていました。
次の投稿では [自動拡張] についてお話しいただいたことについて振り返ってみたいと思います。
12 月 8 日に Microsoft 社が開催している SQL Server の Get The Fact セミナーに参加をしてきました。
SQL Server の真実 – Get The Fact セミナー
このセミナーですがチョークトークの形で開催されており、Microsoft 社 の SQL Server Product Manager チームの方と、かなり近い距離でセッションを受けられるとても貴重な機会でした。
# 質問を Twitter でつぶやくと答えてくれたり。
第 1 回は運用管理について以下の内容についてのセッションでした。
まずは、ログ管理について数回に分けてまとめていきたいと思います。
今回はデータ書き込み時にログがどのように使われるかと、ログの基本的な構成について、セッションの内容をふまえ振り返っていきたいと思います。
トランザクションログについてまとめるにあたり、セミナーでもお話しがあったのですが、データ書き込みの基本動作について軽くまとめてみたいと思います。
SQL Server のデータ書き込みですが、ログキャッシュに書き込み → トランザクションログファイルに書き込み → メモリ上のデータを変更 → チェックポイント発生時にメモリ上のデータをデータファイルに書き込みという流れになります。
# ログキャッシュと、ログファイルへの書き込みタイミングは大抵の場合、タイムラグはさほどないというお話でした。
データ変更時の流れを概要図としてまとめると以下のようになります。
# 概要図なので実際の挙動とは少し違うところがあるのですが。
データ変更 (INSERT / UPDATE / DELETE / TRUNCATE) をした場合、ログに書き込んだ後にすぐにデータファイルを更新するのではなくメモリ上のデータを変更して、その後に実際のデータファイルの更新が行われます。
② の処理でメモリ上のデータは変更されているが、実際のデータファイルが更新されていないデータ (ページ) を [ダーティーページ] (汚れたページ) と呼び、メモリ上のデータをデータファイルに書き込むタイミングを [チェックポイント] と呼びます。
ダーティーページに関しては、以下のクエリで確認をすることが可能です。
|
SELECT |
TEST というデータベースに対してデータ更新を行った際のダーティーページの情報が以下になります。
TEST データベースでメモリを 8 ページ使用しており、そのすべてのページがダーティページとなっています。
# データを新規に INSERT した時のメモリの情報になります。 ![]()
チェックポイントが発生し、メモリ上のデータがデータファイルに書き込まれると、その処理で使用していたログファイルの内容 (変更されたデータの情報) はデータファイルに反映された状態となりますので、そこまでの処理が終了しすることで、ログの内容が消せる状態となります。
メモリ上のデータにしか変更が反映されていない状態で、その変更分のログが消されてしまいますと、メモリの内容が無くなった 場合 (シャットダウンや異常終了等) に、変更内容を戻すことができなくなってしまいますので。
手動でチェックポイント (CHECKPOINT ステートメントを実行) した後に、ページの状態を取得するクエリを実行した時の結果が以下になります。 ![]()
ダーティーページがフラッシュされ、0 になっているのが確認できます。
ダーティーページをフラッシュしても、メモリ上にはキャッシュとしてデータは残った状態になります。
更新されたデータをそのままメモリ上に残しておいた方が、そのデータを使用する処理が発生した場合、ディスクからデータを読まずに済みますので。
ログファイルは [ldf] という拡張子のファイルで構成がされています。
ログファイルは複数の ldf ファイルで構成することも可能なのですが、ログファイルはシーケンシャルに使われますので複数のファイルで構成しても、ファイルが並行で使用され負荷が分散されるという事はありません。
ログファイルを複数用意するのは、負荷分散ではなく (複数ファイルを用意しても負荷は分散されない) ログファイルを格納しているドライブの容量がなくなり、追加のログファイ
ルを設定しなくなてしまった場合になるのかと思います。
# このような状況が発生しないようにするのがベストなのですけどね。
ログファイルは、データファイルとは異なりファイルグループと言った概念がなく、単一のファイルで構成されているのですが内部的には仮想ログファイル (Virtual Log File : VLF) という単位に分割がされています。 ![]()
ログファイルはトランザクションログのバックアップを適切に実行することでこの仮想ログが循環されながら使用されることになります。
データ変更時のログの内容ですが、ログレコードとして管理されており、ログレコードには [LSN] (Log Sequence Number) というシーケンシャル No が振られています。
トランザクションログのログレコードの内容は [DBCC LOG] を実行することで確認をすることができます。
DBCC ログは以下の形式で実行を行います。
| dbcc LOG (dbname | dbid [,{0|1|2|3|4}[,[‘lsn’,'[0x]x:y:z’]|[‘dir’, 0|1]|[‘numrecs’,num]|[‘xdesid’,’x:y’]|[‘extent’,’x:y’]|[‘pageid’,’x:y’]|[‘objid’,{x,’y’}]|[‘logrecs’,{‘lop’|op}…]|[‘output’,x,[‘filename’,’x’]]|[‘column’,'<value>’]|[‘key’,'<value>’]|[‘nolookup’]|[‘allocid’,BIGINT]…]]]) |
ログレコードの詳細まで表示する場合は、オプションとして [4] を設定します。
[DBCC LOG(N’TEST’, 4)] といった形式で実行することでログレコードの内容を含めて取得することが可能です。 ![]()
トランザクションをロールバック / ロールフォワードするときなどは、この LSN を使用することで、どの地点まで戻せばよいのかを判断しています。
データファイルのページでも LSN は保持しており、ページの LSN とログの LSN を比較することで、ロールフォワードの必要があるかを判断しています。
# この辺の詳細は別途まとめる予定です。
ページの情報は [DBCC PAGE] を実行することで、取得ができます。
以下は DBCC PAGE の実行例です。
# DBCC PAGE で情報を取得するためには、トレースフラグ [3604] を有効にする必要があります。
|
DBCC TRACEON(3604) |
|
PAGE: (1:78) BUFFER: BUF @0x0000000085FB5F00 bpage = 0x00000000853A8000 bhash = 0x0000000000000000 bpageno = (1:78) PAGE HEADER: Page @0x00000000853A8000 m_pageId = (1:78) m_headerVersion = 1 m_type = 1 |
ページ情報の [m_lsn] からページが更新された際の LSN を確認することが可能です。
仮想ログにはシーケンシャル No が内部的に振られており、ログレコードはこのシーケンシャル No を考慮しながら仮想ログを先頭から使用しながら書き込まれていきます。 ![]()
このように書き込みが
行われますので、複数のファイルを用意した場合は以下のように使用されます。 ![]()
仮想ログファイルは [DBCC LOGINFO] を実行することで確認することができます。
このクエリを実行すると以下のような情報が取得できます。 ![]()
この情報を確認することで、ログファイルが内部的にいくつの仮想ログに分かれていて、今どの仮想ログが使用されている (再利用ができない) 状態なのかを確認することができます。
Status = 2 は ACTIVE なログが存在している VLF となるため、再利用することができません。
# この辺は SQL Server を実行しているコンピュータでトランザクション ログのサイズが予期せず増大する、または、ログがいっぱいになる に少し記載があります。
この状態の VLF は [トランザクションログのバックアップ] を取得することで、アクティブなログレコードを含んでいない VLF のStatus が 0 (REUSABLE) となります。 ![]()
REUSABLE に変わった VLF は再利用が可能になりますので、新規にログレコードを書き込むことが可能となります。
現在、アクティブなログを含んでいる VLF に関しては、Status = 2 のままですが、アクティブなログを含んでいない VLF に関しては Status = 0 となり再利用が可能となります。
# アクティブなログを含む (Status = 2) の VLF もログレコードが書き込める (空きがある) のであれば利用されます。
ログの切り捨て時 (再利用のために解放) には、[MinLSN] (最小復旧 LSN:データベース全体をロールバックするために必要となる最初の LSN) が切り捨て可能なスタート地点となります。
この、MinLSN から、最後に書き込まれたログまでを [アクティブなログ] と呼び、このアクティブなログが含まれている仮想ログに関しては、データベースの回復に必要となるため、再利用することはできません。
# MinLSN が実際に見れれば説明がしやすかったのですが、ちょっと取得方法がわかりませんでした。
この Status = 0 の状態の VLF ですが、[DBCC SHRINKFILE] を実行した際に、縮小される単位になります。
VLF はログファイルを作成 (あるいは拡張) したタイミングでファイルサイズとログの個数が決まりますので、使用している VLF の状態によっては、SHRINKFILE をしても思ったよりファイルが小さくならないことがあります。
セミナーではログ管理をするにあたって、このようなログの基本的な動作についてのお話を聞くことができました。
# 基本的に振り返りの内容は、私が間違って理解している個所があるかもしれませんので、間違っていた場合はセミナーの内容ではなく私の理解力不足です…。あと、今回の内容をすべてお話しいただいたのではなく、調べる取りかかりの情報をいただけた形になります。
他にも SQL Server のログ管理 (バックアップ) で重要となる [復旧モデル] についてもお話しがありました。
次の投稿では、この復旧モデルについて振り返ってみたいと思います。