TL

不揮発性メモリと永続化 ─ PMEM・キャッシュフラッシュの順序保証

PMEMへ書いたデータが本当に電源断後も残るのか、その分かれ目を原理で押さえられます。ADR/eADRの永続化ドメイン、CLWB+SFENCEのフラッシュ順序、ログによるクラッシュ整合性まで一気通貫で理解できます。

応用不揮発性メモリPMEM永続化ドメインキャッシュフラッシュクラッシュ整合性メモリ順序最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.PMEM(NVDIMM/CXL PMEM)はバイトアドレス可能でCPUのロード/ストアから直接触れるが、ストアした値はまずキャッシュに留まり、電源断時に残るのは『永続化ドメイン』に到達済みのデータだけ。
  • 2.永続化はCLWB/CLFLUSHOPTでキャッシュ行を吐き出し、SFENCEでフラッシュ完了を待ってから次へ進む。ADRはメモリ階層まで、eADRはCPUキャッシュまでを停電時に守る範囲とする。
  • 3.クラッシュ後に半端な状態を残さないには書き込み順序の保証が必須で、データ→フラッシュ→フェンス→コミットフラグの順を守る。アトミック性が要る更新はundo/redoログ等のトランザクションで担保する。

バイトアドレス可能な不揮発メモリとは

従来、不揮発な記憶はブロック単位のストレージ(SSD/HDD)で、CPUはI/Oパスとキューを通してブロックを読み書きしていました。これに対し**永続メモリ(PMEM, Persistent Memory)**は、DRAMと同じメモリバスにぶら下がり、バイト単位でCPUのロード/ストア命令から直接触れる不揮発媒体です。代表が DRAM とフラッシュを同居させた NVDIMM-N や、媒体自体が不揮発な NVDIMM-P、そして近年はメモリをCXLコヒーレント接続経由で拡張する CXL PMEM です。OSはこれを mmap でアドレス空間に直結(DAX, Direct Access)でき、ページキャッシュを介さずアプリが直接書き込めます。

ここに本質的な難所があります。store 命令を実行しても、その値が即座に不揮発媒体へ届くわけではない、という点です。

永続化ドメイン:何が停電を生き残るか

CPUがストアした値は、まずキャッシュ階層(L1/L2/LLC)やストアバッファ、メモリコントローラ内のバッファを順に通り、最後に媒体へ書かれます。途中の段はすべて揮発性です。電源が落ちた瞬間にどこまでが残るかを決めるのが**永続化ドメイン(persistence domain)**で、「ここに入ったデータは停電してもハードが必ず媒体へ書き切る」と保証される境界です。

Intelプラットフォームには2つの段階があります。

機構永続化ドメインに含む範囲停電時の動作ソフト側の責務
ADRメモリコントローラのWPQまで(CPUキャッシュは含まない)残留電力でWPQ内のデータを媒体へフラッシュキャッシュからWPQへ明示的にフラッシュする必要あり
eADRCPUキャッシュ(LLC等)まで含む残留電力でキャッシュ内容も媒体へフラッシュ明示フラッシュ不要、順序づけのフェンスは依然必要

ADR(Asynchronous DRAM Refresh)は、メモリコントローラのWPQ(Write Pending Queue)までを永続化ドメインとします。停電を検知すると、電源装置の残留エネルギーでWPQ内の書き込みを媒体へ吐き切ります。ただしCPUキャッシュは含まれないため、キャッシュに留まったままのストアは失われます。だからソフトウェアが明示的にキャッシュ行をWPQへ追い出さなければなりません。

**eADR(enhanced ADR)**は、より大きな残留電力でCPUキャッシュまで永続化ドメインに含めます。これならアプリは明示フラッシュを省けますが、順序を保証するフェンスは依然として必要です。永続化ドメインの範囲と、操作間の順序保証は別問題だからです。

『書けた』と『残る』は違う

storeが完了し、自コアからその値が読めても、それは媒体に永続化された意味ではありません。値はまだ揮発性のキャッシュにあるだけかもしれません。永続化を主張できるのは、当該キャッシュ行が永続化ドメイン(ADRならWPQ)へ到達したことを保証する手順を踏んだ後だけです。

CLWB/CLFLUSHOPT+SFENCEによる順序保証

ADR環境でキャッシュ行を永続化ドメインへ送り出す命令が、x86のキャッシュ行フラッシュ系です。それぞれ性質が異なります。

命令キャッシュ行の扱い他コアとの順序(serializing)用途
CLFLUSH無効化(evict)して書き戻す強く順序づけられる(遅い)旧来命令、互換目的
CLFLUSHOPT無効化して書き戻す弱順序、後続フラッシュと並列化可大量フラッシュの高速化
CLWB書き戻すが行はキャッシュに残す(保持)弱順序、並列化可直後に再アクセスする永続化に最適

要点は2つあります。第一に、CLWBは行を無効化せずキャッシュに残したまま書き戻すので、永続化した直後にその領域を再び読み書きする典型パターンでミスを増やしません。第二に、CLFLUSHOPT/CLWBは弱順序で、互いに、また周囲のストアと並べ替わり得ます。複数行を続けて吐き出すとき並列に進めて速い反面、「フラッシュがいつ完了したか」は命令の発行だけでは分からないのです。

そこで完了を待つのがSFENCE(ストアフェンス)です。SFENCEは、それ以前に発行した CLWB/CLFLUSHOPT と通常ストアが、フェンスを越えて後続の操作より先に順序づけられることを保証します。永続化の最小手順はこうなります。

store  pmem[addr] = value     // ① 値を書く(まだキャッシュ内)
clwb   pmem[addr]             // ② その行を永続化ドメインへ書き戻し要求
sfence                        // ③ ①②の完了を待つ=ここで初めて永続化が保証

SFENCEを省くと、clwb が裏で進行中のまま次の処理(特に後述のコミットフラグ更新)へ進んでしまい、停電時にコミット済みフラグだけが残ってデータが残らないという最悪の不整合を招きます。eADR環境では clwb は省けますが、操作の前後関係を作るフェンスは残すのが鉄則です。CLWB/CLFLUSHOPT 自体が弱順序であることは、メモリ一貫性モデルで扱う緩和の一種だと捉えると整理しやすいでしょう。

フラッシュとフェンスの役割分担

CLWBは『この行を永続化ドメインへ運べ』という運搬の指示SFENCEは『さっきの運搬が済むまで待て』という順序・完了の壁です。運搬指示だけでは到着を待たないため、両者は必ず対で使います。

クラッシュ整合性と書き込み順序

永続メモリの真の難しさは、単一行の永続化ではなく、複数の永続化操作の途中で停電したときに現れます。停電は任意の瞬間に起こり得るので、どの順序で媒体へ反映されるかを制御しないと、回復不能な中間状態が残ります。

定番が、データ本体と「有効である」ことを示すコミットフラグを別々に書く構造です。順序を誤ると壊れます。

正しい順序:
  data を書く → clwb → sfence → committed=1 を書く → clwb → sfence

もし sfence を挟まず committed を先に永続化してしまうと:
  停電時、committed=1 だけが残り data は古いまま
  → 回復処理は『有効』と誤認し、壊れたデータを読む

つまり「依存する側(フラグ)は、依存される側(データ)が確実に永続化した後でのみ永続化する」という半順序を、フラッシュ+フェンスで明示的に張る必要があります。これはstoreとフラグの公開順序と同型の問題が、可視性ではなく永続性の軸で現れたものです。

ログによるアトミックな更新

しかし1ワードのフラグで守れるのは、書き込みが1回のアトミックな更新で完結する場合に限られます。8バイトを超える更新や、複数箇所をまとめて「全部反映か全部未反映か」にしたい場合、フラグ方式では途中状態が露出します。ここで使うのが**トランザクション(ログ)**です。

  • undoログ(取り消しログ):書き換える前の旧値をログに記録・永続化してから、本体を上書きする。途中で停電したら回復時にログの旧値で巻き戻し、更新が「無かったこと」にする。
  • redoログ(やり直しログ)新値を先にログへ書いて永続化し、コミットマークを付けてから本体へ反映する。停電したらログを再生して更新を「やり切る」。本体反映前なら破棄する。

どちらも核心は同じで、ログの永続化と本体の更新の間に厳密なフェンス順序を置くことです。たとえばredoログなら次の順序を守ります。

redoログの手順:
  log にエントリ書く → clwb → sfence       // ① ログを先に永続化
  commit マークを書く → clwb → sfence       // ② コミット点(ここを越えれば成立)
  本体 data へ反映     → clwb → sfence       // ③ 本体へ適用
  ログを解放(チェックポイント)

①②の間で停電すれば未コミット=破棄、②③の間なら回復時に再生、とコミットマークの永続化を分岐点にアトミック性が担保されます。PMDK(Persistent Memory Development Kit)の libpmemobj などはこのログ管理とフラッシュ順序を内部で行い、アプリは「トランザクション内の更新」を書くだけで済むよう抽象化しています。媒体そのものの不揮発化の物理は新型不揮発メモリの物理が、ブロックストレージ側の整合性手法はstorage-io-path-queuesと合わせて読むと対比が効きます。

アトミック書き込み粒度を超える更新

プラットフォームが保証する『電源断時にアトミックに完了する書き込み』は通常8バイト(アライン済み)です。それを超える更新を、フラッシュとフェンスだけで守ろうとすると、停電時に半分書けた行が残り得ます。範囲をまたぐ更新は必ずログ(トランザクション)でアトミック性を与えてください。

試験・面接のポイント

「ADRはWPQまで/eADRはCPUキャッシュまでを永続化ドメインに含む」「永続化はCLWB(行保持)またはCLFLUSHOPTでフラッシュし、SFENCEで完了を待つ」「CLWB/CLFLUSHOPTは弱順序なのでフェンスで順序を張る」「データ→フラッシュ→フェンス→コミットフラグの順序がクラッシュ整合性の要」「8バイト超のアトミック更新はundo/redoログで担保」が頻出です。

まとめ

  • PMEM(NVDIMM/CXL PMEM)はバイト単位でロード/ストアから触れる不揮発媒体だが、ストアした値はまずキャッシュに留まり、停電を生き残るのは永続化ドメイン到達済みのデータだけ。
  • 永続化ドメインの範囲はADR(WPQまで)かeADR(CPUキャッシュまで)かで決まり、ADRではソフトが明示的にキャッシュ行をフラッシュする。
  • 永続化はCLWB(行を残す)/CLFLUSHOPT(弱順序)でフラッシュ要求し、SFENCEで完了と順序を保証する。フラッシュとフェンスは対で使う。
  • クラッシュ整合性はデータ→フラッシュ→フェンス→コミットフラグの順序が要で、8バイトを超えるアトミック更新はundo/redoログのトランザクションで担保する。

CPU/メモリ/ディスク Article

不揮発性メモリと永続化 ─ PMEM・キャッシュフラッシュの順序保証を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

不揮発性メモリ

比較で見る軸

難易度: advanced / カテゴリ: CPU/メモリ/ディスク / タグ数: 6

導入後に効く点

永続化はCLWB/CLFLUSHOPTでキャッシュ行を吐き出し、SFENCEでフラッシュ完了を待ってから次へ進む。ADRはメモリ階層まで、eADRはCPUキャッシュまでを停電時に守る範囲とする。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
CPU/メモリ/ディスク
タグ数
6

判断チェックリスト

  • 自社の用途が「不揮発性メモリ / PMEM」に近いか確認する。
  • 強みである「PMEM(NVDIMM/CXL PMEM)はバイトアドレス可能でCPUのロード/ストアから直接触れるが、ストアした値はまずキャッシュに留まり、電源断時に残るのは『永続化ドメイン』に到達済みのデータだけ。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

不揮発性メモリPMEM永続化ドメインキャッシュフラッシュクラッシュ整合性不揮発性メモリPMEM永続化ドメイン
参考: 公式情報