[PERFORMANCE]INTERMEDIATE

SLOGを足したのに何も速くならなかった話、と後で気づいた本当の危険

自宅の`homenas`でNFS共有を少し速くしたくて余っていたコンシューマ向けNVMeをSLOGにしたら、体感速度がまったく変わりませんでした。理由は単純で、そのとき動いていたのはほぼ非同期書き込みだったからです。もっと怖かったのは後から知ったPLP(電源損失保護)の話でした。ZILとSLOGの役割分担、SLOGが効く条件、そして「PLPなしSSDをSLOGにする」という最も危険な落とし穴を整理します。

自宅のhomenasでNFS共有が少しもたつくのが気になり、余っていたコンシューマ向けNVMeをSLOGとして足したことがあります。結果は体感ゼロ。当時のワークロードはバックアップのrsync転送が中心で、これはほぼ非同期書き込みだったからです。SLOGが効くのは同期書き込みのACK待ち時間という非常に限定された経路だけで、そこを通らないワークロードには一切効きません。

もっと後になって血の気が引いたのは、そのNVMeにPLP(電源損失保護)がないと知ったときです。もし本番相当のDBワークロードでこれを使っていたら、という話は後半で書きます。この記事では、ZIL(ZFS Intent Log)とSLOG(Separate Log device)がそれぞれ何をしているのか、どういうワークロードで初めて意味を持つのか、そして導入時に見落とすと危険な「デバイス選定」の落とし穴を整理します。

すべてのプールに最初から存在するログ

ZIL(ZFS Intent Log)は、同期書き込み要求(fsync()など)が発生したときに、実際のtxg(トランザクショングループ、既定5秒周期)コミットを待たずにACKを返すための仕組みです。ZFSのプールにはデフォルトで専用デバイスなしのZILが最初から存在しており、これは「in-pool ZIL」と呼ばれます。in-pool ZILは、プールを構成するデータvdev自身の一部の領域にZILエントリを書き込みます。

ここで重要な誤解を先に解いておきます。ZILは書き込みキャッシュではありません。同期書き込みで書かれたデータは、いずれにせよ通常のtxgコミットを通じて本来の格納先へ書き込まれます。ZILエントリが読み返されるのは、クリーンでないシャットダウン(クラッシュ・停電)の直後、プールをインポートするときだけです。txgが正常にコミットされていれば、ZILエントリはすぐに不要になり破棄されます。つまりZILの役割は「クラッシュしてもACK済みの同期書き込みを失わないための、短命な保険」に限られます。

SLOGはZILの置き場所を専用デバイスに移すだけ

SLOG(Separate Intent Log、単に「log device」とも呼ばれます)は、このZILエントリの書き込み先をデータvdevから切り離し、専用の低レイテンシデバイスに移すためのvdevです。

zpool add tank log /dev/nvme0n1

SLOGを追加しても、プールの実効容量は増えません。SLOGが変えるのは、同期書き込みのACKが返るまでのレイテンシだけです。in-pool ZILでは、ZILエントリの書き込みが通常のデータvdev I/Oと同じディスクを奪い合うため、ワークロードが混雑しているときほどACKが遅くなります。SLOGはこの競合を切り離し、専用デバイス(多くの場合、低レイテンシなNVMe)にZILエントリを書くことで、ACKまでの時間を短く安定させます。

3つの経路(非同期書き込み・同期書き込みSLOGなし・同期書き込みSLOGあり)を比較すると、次のようになります。

書き込み要求の経路: 非同期 / 同期(SLOGなし) / 同期(SLOGあり)

① 非同期書き込み(通常のwrite)write()ARC上でdirty(RAM)txgで一括commit(既定5秒周期)ZIL/SLOGは経路に登場しない。ACKはRAMに書けた時点で即返る(txg commit前でもアプリからは成功に見える)② 同期書き込み・SLOGなしfsync()ZILエントリをプール本体に書込ACK(データvdevに書けた時点)ZILがデータvdevと同じディスクI/Oを奪い合う。通常I/Oと競合し、ACKまでのレイテンシが伸びやすい③ 同期書き込み・SLOGありfsync()ZILエントリをSLOG専用デバイスに書込ACK(SLOGに書けた時点、低レイテンシ)データvdevのI/Oと競合しない専用デバイスに書くため、ACKが速く安定する

→ SLOGが変えるのは③のACKまでのレイテンシだけ。①の非同期書き込みには一切関与せず、データ本体の最終的な格納先も②③とも同じ(通常のtxgコミット経由)。

そもそも同期書き込みでなければ何も変わらない

冒頭のhomenasの話がまさにこれでした。SLOGの効果を語る前に確認すべきは、「対象のワークロードが実際に同期書き込みを発生させているか」です。一般的なアプリケーションの書き込みの大半は非同期(バッファ書き込み)であり、この経路はZIL/SLOGを一切通りません。rsyncのバックアップ転送はまさにこの非同期側で、SLOGを足したところで速くなりようがなかったわけです。

同期書き込みが発生する代表的なケースは次のとおりです。

発生源同期書き込みになる理由
NFSサーバーNFSプロトコルの仕様上、既定で同期的な書き込みコミットを要求する
データベース(PostgreSQL/MySQL等)トランザクションコミット時にfsync/fdatasyncでWAL/ログを確定させる
VM/ハイパーバイザーのディスクイメージwriteback以外の同期キャッシュモードで動作させている場合
アプリケーションの明示的なfsync()/O_DSYNC呼び出し耐障害性のためデータ確定を明示的に要求している

逆に、単純なファイルコピー(cp)や一般的なシーケンシャル書き込みバッチ処理は非同期書き込みが主体であり、SLOGの恩恵をほとんど受けません。導入前に、対象ワークロードがこれらの同期書き込み発生源に該当するかを確認するのが最初のステップです。

sync プロパティ: 挙動を制御する3値

ZFSのsyncプロパティは、アプリケーションからの同期/非同期の要求をどう扱うかを制御します。

挙動
standard(既定)アプリケーションの要求どおりに同期/非同期を扱う
alwaysすべての書き込みを同期扱いにする(既定では非同期のものも含む)。ACK待ちが増え性能が大きく低下するため、通常は使わない
disabled同期要求を無視し、すべて非同期として扱う

sync=disabledは、「SLOGを買わずに同期書き込みを速く見せる」近道として使われることがありますが、これはアプリケーションが「確定した」と思っているデータが、実際にはtxgコミット前のクラッシュで失われうることを意味します。データベースのコミット済みトランザクションが電源断で消える、という耐障害性上の契約違反に直結するため、検証用の使い捨て環境や、失われても構わない一時領域以外では使うべきではありません。

zfs set sync=disabled tank/scratch

PLPなしのSSDは「壊れていないのに壊れている」

homenasに足したあのコンシューマ向けNVMe、後になって型番のデータシートを見返したらPLP(電源損失保護)の記載がありませんでした。幸い当時は非同期書き込みしか通していなかったので実害はなかったのですが、もしあのまま気づかずVM datastoreの同期書き込み用に転用していたらと思うとぞっとします。SLOGのデバイス選定で最も見落とされやすく、かつ最も危険なのがこの**電源損失保護(PLP: Power-Loss Protection)**の有無です。

多くのコンシューマ向けSSD/NVMeは、書き込みデータが実際にフラッシュメモリへ確定する前に、揮発性のDRAMキャッシュに書き込んだ時点で完了(ACK)を返します。通常の利用では問題になりませんが、SLOGとして使う場合はこれが致命的です。ZFSはSLOGへの書き込みACKを「そのデータは確定して安全である」という前提でアプリケーションにACKを返しています。PLPを持たないデバイスが電源断でDRAMキャッシュの内容を失うと、アプリケーションには「同期書き込みが成功した」と伝えているのに、実際にはデータが失われているという、同期書き込みの契約そのものが壊れた状態になります。

これは「SLOGを足していないより悪い」結果です。SLOGなしであれば少なくとも通常のtxgコミットの保証しかないと分かっていますが、PLPなしSLOGは「安全なはずが実は安全でない」という偽りの安心感を生みます。SLOGに使うデバイスは、以下の条件を満たすものに限定してください。

  • PLP(電源損失保護)を持つこと。 キャパシタでDRAMキャッシュの内容を電源断後も確実にフラッシュへ書き切れる、エンタープライズ/データセンター向けNVMe・SSDが対象。コンシューマ向け製品の多くはPLPを持たない
  • 小ブロック・低キューデプスでの書き込みレイテンシが低いこと。 ZILエントリは小さく頻繁な書き込みが中心のため、シーケンシャルスループットの高さより、この特性が重要
  • 高い書き込み耐久性(DWPD)を持つこと。 SLOGは常時小さな書き込みを受け続けるため、QLCなど耐久性の低いコンシューマ向けメディアは寿命面で不向き

大容量のNVMeを積んでも無駄になりやすい

L2ARCとは対照的に、SLOGは大容量である必要がありません。SLOGに保持されるのは、直近でtxgコミットされていない同期書き込み分のZILエントリだけであり、txgが確定すればすぐに不要になります。目安としては、想定される同期書き込みスループット × txg間隔(既定5秒)の数倍程度あれば十分で、多くの環境では数GB〜十数GB程度で足ります。「余っているから」という理由で大容量のNVMeをまるごとSLOGに割り当てるのは、レイテンシ・PLP・耐久性の条件さえ満たしていれば無駄にはなりませんが、容量そのものが性能に寄与するわけではない点は理解しておく必要があります。

ミラー化するかどうか

SLOGデバイスが通常運用中に故障しても、ZFSは自動的にin-pool ZILへフォールバックするため、プール自体が失われることはありません。リスクが生じるのは、SLOGデバイスがクリーンでないシャットダウン(クラッシュ・停電)とほぼ同時に故障した場合で、この狭い時間窓に限り、ACK済みの直近の同期書き込みが失われる可能性があります。

この窓を許容できない、耐障害性の要件が厳しいデータベースクラスタなどでは、SLOGをミラー構成にすることを検討します。

zpool add tank log mirror /dev/nvme0n1 /dev/nvme1n1

一方で、この窓は「クラッシュとSLOG故障がほぼ同時に起きる」という複合条件であり、発生確率自体は低いため、要件が特に厳しくない環境ではミラー化までは行わず、単一デバイスで運用する判断も実務上は珍しくありません。

SLOGを検討する前に確認すること

  • 対象ワークロードが実際に同期書き込みを発生させているか(NFS/DB/VM同期キャッシュ等)。該当しなければSLOGは無意味 — 自分がまさにこれで空振りしました
  • 現状のACKレイテンシが同期書き込みのボトルネックになっているか(zpool iostatzilstat等での実測)
  • SLOG候補デバイスがPLP(電源損失保護)を持つか。データシートを必ず確認する。持たない場合は候補から除外する
  • 小ブロック・低キューデプスでの書き込みレイテンシと、DWPD(書き込み耐久性)
  • 必要容量は想定同期書き込みスループット × txg間隔の数倍程度で足りる(大容量である必要はない)
  • 耐障害性要件次第でミラー化するか
  • sync=disabledを安易な近道として使っていないか

homenasのNVMeは結局、PLPを持つ中古のエンタープライズ品に買い替えました。読み込み側のキャッシュ設計(ARC/L2ARC)と対になる書き込み側の判断として、あわせて考えておくとよいと思います。