神谷 雅紀
Escalation Engineer
SQL Server 2012 以降では、以下のメッセージが Errorlog ファイルに記録されることがあります。今回はこのメッセージを解説します。
メッセージ
FlushCache: cleaned up 39907 bufs with 1628 writes in 74882 ms (avoided 30218 new dirty bufs) for db 5:0
average writes per second: 21.74 writes/sec average throughput: 4.15 MB/sec, I/O saturation: 3296, context switches 9000
last target outstanding: 153600, avgWriteLatency 22
メッセージの意味
このメッセージは、チェックポイントプロセスが、データバッファにキャッシュされているデータをデータファイルへフラッシュするのに、recovery interval に設定されている時間よりも長い時間がかかったことを示しています。
上のメッセージを例にすると、「データベース ID 5 のキャッシュ上で変更されたデータページのうち チェックポイント開始後に変更された 30218 ページを除く 39907 ページを、74882 ミリ秒かけて 1628 回の書き込み要求によりファイルへ書き出した。平均書き込み数は 21.75 回 / 秒、平均スループットは 4.15 MB / 秒、未完了 I/O 数がターゲットを超えたのは 3296 回、コンテキストスイッチは 9000 回、未完了 I/O ターゲットは 1536000、平均書き込みレイテンシーは 22」という意味です。
ここで言う「未完了 I/O ターゲット」は、未完了非同期 I/O の最大数です。未完了 I/O 数がこの最大数を超えると SLEEP_BPOOL_FLUSH 待ちとなります。
このメッセージが記録された時の対応
このメッセージが記録された時に実施すべき対応は、まず、ディスクのパフォーマンスを確認することです。パフォーマンスカウンタで、SQLServer:Buffer Manager\Checkpoint pages/sec と該当データベースのデータファイルが置かれている物理ディスクの Physical Disks\Avg. Disk Write Queue Length や Current Disk Queue Length、%Idle Time を確認し、チェックポイント時の Idle Time が 0 でディスクキューが非常に長い場合は、そのデータベースが必要とする書き込み量がディスクの性能を上回っている可能性があります。
書き込み量がディスク性能を上回っていると思われる場合、ディスク自体の性能を向上させる以外では、以下の方法が考えられます。
-k 起動パラメーターによる最大 I/O 量の制限
SQL Server では、起動パラメーター –k により、チェックポイントによる書き込み量を制限することができます。例えば、起動パラメーターとして -k5 を指定すると、チェックポイントによる書き込みは、5 MB / 秒に制限されます。
これにより、ディスク性能を上回る書き込み要求を発生させないようにすることができる可能性がありますが、チェックポイントによる書き込み自体の絶対量が減る訳ではないため、1 度のチェックポイントにかかる時間が長くなる可能性があります。
間接チェックポイント
SQL Server 2012 以降では、間接チェックポイントを使用することで、自動チェックポイントよりも短い間隔でチェックポイントを実行することができます。これにより、一度のチェックポイントでファイルに書き出すページ数を減らすことができる可能性があります。例えば、これまではチェックポイントの平均実行間隔が 1 分で 100 ページの書き出しを行っていたものを、30 秒で 50 ページを書き出すといった形です。
ただし、頻繁にページが書き換わる場合、全体としての書き込み量が多くなる可能性もあります。例えば、1 秒に 1 度書き換わるページがある場合、チェックポイントの平均実行間隔が 1 分であったとすると、ページの内容がメモリ上で 60 回書き換わって 1 度ファイルに書き出されることになります。この場合、チェックポイントの平均実行間隔が 30 秒になっても、30 回書き換わって 1 度ファイルに書かれるようになるだけで、どちらの場合もチェックポイント時の書き出し対象ページであることには変わりません。その結果、チェックポイント間隔が短くなることで、合計の書き出しページ数が増える可能性もあります。このような点も考慮して、TARGET_RECOVERY_TIME を決定する必要があります。