====== ZFS Fragmentation issue – examining the ZILの翻訳 ====== [[blog:2014:2014-11-03|前回]]に引き続き、ZFSのフラグメンテーションを調査してて有益な記事を見つけた。これまた筆者のThomas Gouverneur氏に翻訳の許可を貰ったので翻訳してみる。 翻訳には細心の注意を払っていますが、誤訳や変な箇所があればご指摘下さい。例によって、本記事を利用した事で生じた如何なる結果について、翻訳者は責を負いかねます。また本翻訳について原著者への問い合わせはご遠慮願います。 ===== ZFSの断片化問題 - ZILの分析 ===== //Original Post: [[http://thomas.gouverneur.name/2011/06/20110609zfs-fragmentation-issue-examining-the-zil/|ZFS Fragmentation issue – examining the ZIL]]// \\ //Author: Thomas Gouverneur// \\ //Date: June 9, 2011, 2:32 pm// \\ このところ私は、重いデータベースで使われている幾つかのZFSプールのトラブルシューティングに取り組んでいました。 顧客から報告された問題は、クラスタの再起動を最後に性能が著しく低下したというものでした。 この問題は遂に解決し、そして原因はZFSの断片化問題だと特定するに至りました。 まず最初に、ドリルダウン方式でその性能問題を調査しました。TeamQuestを用いて現在と前週の違いを視覚化し私たちが見たのは、データベースのDBFがあるプールのI/Oの、文字通りの爆発でした。 ある時は、その病んだプールの異なるvdevを均等に横切る50K IOPS以上の書き込みがありました。 プールは実際に次のように構成されていました: # zpool status i-ora-pro06-dat1-pl pool: i-ora-pro06-dat1-pl state: ONLINE scrub: none requested config: NAME STATE READ WRITE CKSUM i-ora-pro06-dat1-pl ONLINE 0 0 0 mirror-0 ONLINE 0 0 0 c6t60060E800571FC00000071FC000020C3d0 ONLINE 0 0 0 c6t60060E800570FB00000070FB000020C3d0 ONLINE 0 0 0 mirror-1 ONLINE 0 0 0 c6t60060E800571FC00000071FC000020C4d0 ONLINE 0 0 0 c6t60060E800570FB00000070FB000020C4d0 ONLINE 0 0 0 mirror-2 ONLINE 0 0 0 c6t60060E800571FC00000071FC000020C5d0 ONLINE 0 0 0 c6t60060E800570FB00000070FB000020C5d0 ONLINE 0 0 0 mirror-3 ONLINE 0 0 0 c6t60060E800571FC00000071FC000020C6d0 ONLINE 0 0 0 c6t60060E800570FB00000070FB000020C6d0 ONLINE 0 0 0 errors: No known data errors 背後にあるSANディスクは大量のI/Oを捌くことが可能で、またSANはあらゆる不具合について点検がなされましたが異常はなく、この問題はLUNにおける大量のI/O処理負荷でした。 続いて、私たちの考えの裏付けを取るために''zpool iostat -v i-ora-pro06-dat1-pl 2''を実行し続けました。 これにより、vdevに対する激しい書き込みを確認しました。 再びTeamQuestで、私たちはディスクに行われた書き込み操作の種類を見る事ができ、実際に見たところ、それらは非常に小さなブロックの書き込みでした。 その後、私たちはオラクル-SUNサポートのサポートケースを開き、私たちが向き合う問題が露見している幾つかのGUDSトレースをアップロードしました。 本問題の検出・修正方法はもちろん、完全な説明を以下に述べます。 基本的に、この問題は“ZFS断片化”と呼ばれています。 ZFSが話題の利用可能なドキュメントで断片化の内部について言及した物はありませんが、これはどうして起るのでしょうか? 無論、ZFS断片化の検出や解決のためのツールはありません。 断片化が如何にして、利用が適さないまでにプールを退化させ得るかを少しばかり理解するために、私はZIL (ZFS Intent Log)とZILのプール内での扱われ方について、今から話します。 また、プールを使うOracleデータベースがどのように振る舞うかについての、いくつかの基礎的な情報から始めます。 ==== 1. Oracleデータベースの挙動 ==== 問題のあるZFSプールで実行中のデータベースは、私の顧客企業内で最も重要なDBのひとつでした。 それは“リアルタイム”データベースと見なされますが、それはつまり大量のINSERTが毎秒行われる事を意味します。 このデータベースはまた、その会社のビジネスの上で非常にクリティカルな事柄をモニターしていました。 それらの関係は、フロントエンドでのINSERT指示とデータベースへの実際の反映の間で、1時間の遅れがあるというものでした。 Oracle DBの階層では、データベース書き込みスレッド群が、ディスクの大変なサービス時間が原因で書き込み操作を抱えている事実を除き、特に何も見られませんでした。 Oracle DB自身は、ある意味ではあらゆる書き込み操作後にディスクを使用しますが、データベースが''fsync()''を呼ぶと、OSによる書き込み操作のディスクへの直接コミットが生じます。 これは全てのOracleデータベースについて言えます。 ==== 2. ZILの挙動 ==== 既にご存知の方もいるでしょうが、ZFSはUFSファイルシステムの古いジャーナルをZIL (ZFS Intent Log)と呼ばれるものに置き換えました。 要するに、プールに対する全ての操作は、プール操作のリクエスタの完了を保証するためにZILトランザクションを引き起こします。 従って、書き込み操作の場合、実行予定の全ての操作を含む1つのZILブロックが作成され、このZILトランザクションが完了するとすぐに、リクエスタはリクエストの成功を告げられます。たとえ実際にはデータのプールへの書き込みが未完了だったとしてもです。 ZILトランザクションは、処理が適切に行われたであろう事を保証するでしょう。 ZILトランザクションのコミット後、関連するZILブロックは解放され、このZILトランザクションによってディスクに確保された一時領域は放出されます。 ZILレベルで思いがけず生じる他のメカニズムもありますが、それらは本記事の後でカバーします。 ZFSファイルシステムのアンマウント毎にZILはチェックされ、ディスクへコミットされ、そして完全に解放されます。 マウント毎にZFSはZILエントリーの存在を確認しますが、エントリーがあれば、それはファイルシステムが正しくアンマウントされなかった事を示し、ファイルシステムがマウントされる前に見つかったZILエントリーはコミットされます。 リクエスタが''fsync()''を発行すると、ZILトランザクションはワークメモリの代わりに強制的にディスクに書き込みされるという事も知っておくと良いでしょう。 ==== 3. どのように断片化が起こるのか? ==== データベースが大量のINSERTを行い続けると、大量のZILトランザクションの作成、プールのディスクへの一時的な書き込み、そして削除に直結します。 とても小さいブロックの大量確保と解放がZILトランザクションのために発生し、それがディスクに連続して書かれるべき本来のデータに対して何の役にも立たないであろうことは、容易に理解出来ます。 それでも、これは問題ではありません。 それらの実行時間の果てに、ZILのトランザクションエントリーに連続ブロックを確保出来なくなる時が訪れます。 そうして、ギャングブロック機構が働きます。 ギャングブロックはZILの一部で、そしてまさに断片化したZILブロックです。 1つのギャングブロックは、1つのヘッダと最大3つのギャングメンバーから成ります。 ヘッダは、ZILトランザクションのデータを実際に保持するギャングメンバーへのただのポインタです。 ヘッダはただ1つのブロックのみを消費し、それ故にプールの断片化レベルにかかわらず確保することが出来ます。 他に留意すべき事は、ギャングブロックは入れ子となり得るため、ギャングメンバーもまたギャングブロックから構成することが出来ます。 ギャングブロック使用中、ZILは必要とされるたびに、ギャングツリー全体のメモリへの読み込みを強いられます。 ZILトランザクションがディスクに反映される時、ギャングブロックの削除は各ギャングメンバーとその子供の削除を意味します。 これが断片化の生じる場所です! ディスクに確保と解放が行われる全てのZILトランザクションは、ZILエントリーおよびプールのデータの間に隙間を生じます。 我々の''zpool iostat''が以前に報告した膨大な書き込み操作数についても説明します。 これら書き込み操作はディスクを読み込み、そしてアプリケーションが使用可能なプールの実帯域幅を狭めます。 ==== 4. 発生中の問題はどのように見えるか? ==== 問題を見るために、私たちはZILがどのように作用しているか見るべきです。 私たちが通常の方法でZILトランザクションの生成と削除を見るだけでは、どんな極度の断片化の発生も推定する事は出来ません。 別の方法で、私たちはより深くへ行くことが可能です。 実際にギャンギングがシステム全体のレベルで問題を起こしているかどうか見つけ出すには、''lockstat''を使います: # /usr/sbin/lockstat -CcwP -n 50000 -D 20 -s 40 sleep 2 ギャングブロックによる問題がシステムに本当にあるならば、あなたは次のような項目を目にするでしょう: # grep gang lockstat-* lockstat-C.out: 32768 |@@ 26 zio_gang_tree_assemble_done+0x74 lockstat-C.out: 262144 | 7 zio_gang_tree_assemble_done+0x74 lockstat-C.out: 4096 |@@@@@ 146 zio_gang_tree_issue+0x78 lockstat-C.out: 8192 |@@@@@@@@@@@@@@@@@@@@@ 586 zio_gang_tree_issue+0x78 lockstat-C.out: 16384 | 13 zio_gang_tree_issue+0x78 lockstat-C.out: zio_gang_tree_issue+0x78 lockstat-C.out: zio_gang_tree_issue+0x78 lockstat-C.out: zio_gang_issue+0x48 lockstat-C.out: 2048 | 14 zio_gang_tree_issue+0x78 lockstat-C.out: 4096 |@@@@@@@ 192 zio_gang_tree_issue+0x78 lockstat-C.out: 8192 |@@@@@@@@@@@@@@@@@@@@ 496 zio_gang_tree_issue+0x78 lockstat-C.out: 16384 | 1 zio_gang_tree_issue+0x78 lockstat-C.out: zio_gang_tree_issue+0x78 lockstat-C.out: zio_gang_issue+0x48 lockstat-C.out: 2048 |@@ 11 zio_gang_tree_assemble_done+0x74 lockstat-C.out: 16384 |@@@@ 18 zio_gang_tree_assemble_done+0x74 lockstat-C.out: 4096 |@ 2 zio_gang_tree_assemble+0x5c lockstat-C.out: 8192 |@@@@@@@@@@ 15 zio_gang_tree_assemble_done+0x74 lockstat-C.out: 65536 | 0 zio_gang_tree_assemble_done+0x74 lockstat-C.out: 2048 |@@ 3 zio_gang_tree_assemble+0x5c lockstat-C.out: 4096 |@@@@ 6 zio_gang_tree_assemble_done+0x74 lockstat-C.out: 32768 | 2 zio_gang_tree_assemble_done+0x74 lockstat-C.out: 262144 | 1 zio_gang_tree_assemble_done+0x74 lockstat-C.out: zio_gang_tree_assemble_done+0x74 この時点から、実際にギャングブロックの問題を持っていると認められます。 ==== 5. 問題の修正 ==== この問題を完全に直すには、UFSの断片化したファイルシステムがそうであったように、正攻法はプールを再作成する事です。 問題の再発を防ぐための解決方法は、ミラー(もしくは非ミラー)のログデバイスをプールに追加します。これによりZILトランザクションはログデバイスに書き込まれ、プールのデータ用vdevの断片化を避けられます。 このケースが上手く動くのは、追加したログデバイスがオンラインになり、進行中のZILトランザクションがフラッシュされるまで少し待ち、そして新規トランザクションが独立したデバイスへ書き込まれるようになってからです。 注意: この解決策は性能向上の助けとなりますが、ZILが引っ掻き回した後のゴーダチーズのようなデータvdevのデフラグにはなりません。 今のところは、直面した問題への手助けとなっています。プール再作成にはダウンタイムを要しますが、デバイスの追加はそうではないので。 ==== 6. Proactive actions ==== プールのギャングブロックの使用状況について時々システムを監視することで、あなたは積極的にこの問題に対して取り組むことが可能となります。 # dtrace -qn 'fbt::zio_gang_tree_issue:entry { @[pid]=count(); }' -c "sleep 300" 26574 61 0 7141 26575 18949 26570 416399 # ps -eaf|egrep "26574|26575|26570" root 26574 0 0 May 26 ? 2778:02 zpool-i-ora-pro06-arc1-pl root 26570 0 0 May 26 ? 9155:49 zpool-i-ora-pro06-dat1-pl root 26575 0 0 May 26 ? 574:51 zpool-i-ora-pro06-rdo1-pl # 注意: 時としてギャンギングは平静を装いますが、これらプールはまだギャンギングの影響は出ていないようです。 しかし、実際にギャンギング問題が出たプールを特定するための多少の経験になったかもしれません。そして、ZIL断片化が本当の性能問題に変わる前に、先を見越してプールにログデバイスを追加する措置を取るのが良いかもしれません。 ==== 7. 謝辞==== * Openindiana source code * #opensolaris-fr @ freenode ==== 8. 参考 ==== * [[http://blogs.oracle.com/perrin/]] * [[http://wildcat.espix.org/txt/refs/zio.c]] * [[http://wildcat.espix.org/tmp/guds_2.9.1]] * [[http://blogs.oracle.com/realneel/entry/the_zfs_intent_log]] * [[http://wildcat.espix.org/tmp/gang_blocks.txt]] * [[http://sunsolve.espix.org/bugid/id/6598837]] * [[http://sunsolve.espix.org/bugid/id/6596237]]