ライトアンプリフィケーション

データベースへの 1 回の論理的な書き込みが、内部的に複数回の物理的な書き込みを引き起こす現象

データベースパフォーマンス

ライトアンプリフィケーションとは

ライトアンプリフィケーション (Write Amplification) は、アプリケーションが 1 回の論理的な書き込みを行った際に、データベースやストレージが内部的に複数回の物理的な書き込みを行う現象である。書き込み増幅率 (Write Amplification Factor・以下 WAF) が高いほど、同じ論理書き込み量に対して消費される帯域と NAND の消去回数が増えるため、書き込みスループットが落ち、SSD の寿命 (ブロックあたりの消去回数の上限) を早く使い切る。なお WAF という略語は Web Application Firewall とも衝突するので、文脈で読み分ける必要がある。

WAF = 物理的な書き込み量 / 論理的な書き込み量

例: 1KB のデータを書き込むのに、内部的に 10KB の書き込みが発生
WAF = 10KB / 1KB = 10

発生する場面

RDB のインデックス更新

1 行を INSERT すると、テーブル本体だけでなく、その行を含む全インデックスの更新が必要になる。インデックスが 5 つあれば更新対象は 6 箇所である。

ただしこれは論理的な更新箇所の数であって、物理的な書き込み回数と一対一では対応しない。多くの RDB は更新をまず WAL (先行書き込みログ) へ順次追記し、実データはバッファプール上のページを書き換えて後からまとめてフラッシュする。増幅は「INSERT 1 回 = 6 回の I/O」という形では現れず、WAL への追記量とダーティページのフラッシュ量の両方に効いてくる。

-- テーブルにインデックスが 5 つある場合
INSERT INTO orders (id, user_id, status, created_at, total)
VALUES ('ord-1', 'user-1', 'pending', NOW(), 5000);
-- 更新対象: テーブル本体 + 5 インデックス = 6 箇所

LSM ツリー (Cassandra・RocksDB など)

LSM ツリー (Log-Structured Merge-tree) を使うデータベースでは、書き込みはまずメモリ上の MemTable に入り、一定量たまるとディスク上の SSTable として書き出される。その後、バックグラウンドのコンパクション (マージ) が同じデータを下位のレベルへ何度も書き直す。1 件のデータがレベルを降りるたびに書き直されるので、増幅はレベル数と隣接レベルの容量比 (fanout) に沿って積み上がる。

RocksDB の公式ドキュメントは、代表的な leveled コンパクションについて、レベルあたりの書き込み増幅は最悪ケースで fanout に等しく、実際にはそれより小さくなる傾向があると説明している。leveled は空間増幅を小さく抑える代わりに読み取り増幅と書き込み増幅を払う方式であり、どの増幅を犠牲にするかはコンパクション方式の選択そのものである。

書き込み → MemTable → SSTable L0 → コンパクション → SSTable L1 → コンパクション → SSTable L2
                                    ↑ 再書き込み        ↑ 再書き込み

SSD のガベージコレクション

NAND フラッシュは書き込みの最小単位 (ページ) と消去の最小単位 (ブロック) が異なり、ブロックはページの数百倍の大きさになる (具体的な値は世代や製品で変わる)。しかも一度書いたページは、ブロックごと消去しない限り上書きできない。

そのため SSD は、あるページの内容が更新されたとき、その場を書き換えず、空きページへ新しい内容を書いて FTL (Flash Translation Layer) が持つ論理アドレスと物理アドレスの対応表を差し替える。古いページはその瞬間に「無効」になるだけで、消去はされない。

無効ページがたまって空きブロックが減ると、ガベージコレクションが動く。ここで初めて、消去したいブロックに残っている有効ページを別のブロックへコピーし、元のブロックをまとめて消去する。この退避コピーがホストの意図しない物理書き込みであり、SSD 側の増幅の主因である。

順序を取り違えないことが大事で、更新時に起きるのは別ページへの追い書きであり、ブロック単位のコピーは後のガベージコレクション時である。この 2 段構えを「1 ページの更新でブロック全体が書き直される」とまとめてしまうと、空き容量に余裕がある SSD の方が速くて長持ちする理由が説明できなくなる。オーバープロビジョニング (ユーザーに見せない予備領域) を厚く取ると、消去対象ブロックに残る有効ページが減って退避コピーが少なくなるため増幅が下がる。TRIM (削除済み領域の通知) も、SSD に「このページはもう有効でない」と伝えて退避コピーの対象から外させる仕組みである。

2 つの増幅は別の層で起きる

データベース層の増幅 (インデックス更新・コンパクション) と SSD 内部の増幅 (FTL とガベージコレクション) は、原因も対策も別物である。前者は論理的な書き込み量そのものを増やし、後者は与えられた論理書き込みを NAND へ落とす過程で増やす。両者は打ち消し合わずに掛け算になるので、LSM ツリーのデータベースを空き容量の少ない SSD に載せると 両方の増幅が同時に効く。切り分けるときは、データベースが出した書き込み量 (OS から見た書き込みバイト数) と SSD が実際に NAND へ書いた量 (SMART の属性として読める製品がある) を別々に見る。

DynamoDB での影響

DynamoDB では、内部のストレージ構造よりも、課金単位の丸めと GSI の更新が「1 回の書き込みが何回分になるか」を決める。開発チーム自身が 2022 年の USENIX ATC で発表した論文では、ストレージレプリカが先行書き込みログとキーバリューデータを収める B ツリーを持つ構造として説明されており、2026 年 8 月時点で公開されている情報の範囲では、LSM ツリーのコンパクションを前提にした説明は当てはまらない。

増幅として体感するのは次の 2 点である。

  • 課金単位の丸め: 1 WCU は 1 KB までのアイテムに対する 1 秒あたり 1 回の書き込みを表す。アイテムサイズを 1 KB で割って切り上げるため、512 バイトのアイテムでも 1 WCU を消費する
  • GSI の更新: テーブルへの書き込みによって GSI 側の項目が変わる場合、その GSI の書き込み容量が別に消費される

GSI のコストは「GSI の数」では決まらない。AWS の公式ドキュメントは、必要な書き込み回数を次のように整理している。

テーブル側の操作その GSI で必要な書き込み
インデックス対象属性を持つ項目を新規追加 (または未定義だった属性を定義)1 回 (投入)
インデックスのキー属性の値が変わる2 回 (旧項目の削除 + 新項目の投入)
書き込みによってインデックス対象属性が削除された1 回 (旧項目の削除)
更新の前後どちらもインデックスに載らない0 回
射影済み属性だけが変わり、キー属性は変わらない1 回 (射影値の更新)
キー属性でも射影対象でもない属性だけが変わる0 回 (テーブル側だけ課金)

したがって GSI を 3 つ持つテーブルでも、更新した属性がどの GSI にも関係しなければ追加コストはゼロで、逆にキー属性を書き換えると 1 つの GSI だけで 2 回分かかる。上の回数はいずれもインデックス項目のサイズが 1 KB 以下である前提で、それを超えると追加の WCU が必要になる。GSI の更新は非同期の結果整合で反映されるが、GSI 側の容量が足りないとテーブルへの書き込み自体がスロットリングされる点も設計上の落とし穴である。

WAF を低減する方法

打ち手は「論理的な書き込みを減らす」層と「物理的な増幅を減らす」層に分かれる。どちらに効く手なのかを意識しないと、SSD 側が原因の問題にインデックス削減で対処するような空振りになる。

対策効く層と効き方
インデックス・GSI を必要な分だけに絞る論理層。更新すべき箇所そのものを減らす
更新頻度の高い属性をインデックスのキーと射影から外す論理層。その更新での GSI 側の書き込みを 0 回にできる
バッチ書き込み・書き込みバッファリング論理層。小さな書き込みをまとめ、課金単位やページ単位の切り上げによる損を減らす
SSD の空き容量とオーバープロビジョニングに余裕を持たせる物理層。ガベージコレクションの退避コピーを減らす
コンパクション方式を選び直す (LSM ツリー系)論理層。書き込み増幅・読み取り増幅・空間増幅のどれを犠牲にするか選ぶ

パーティションキー設計はよく同じ文脈で語られるが、これはホットパーティションによるスロットリングへの対策であって増幅そのものを減らす手ではない。混ぜて考えると、増幅が原因の遅さをキー設計で直そうとして時間を失う。

読み取りアンプリフィケーションとのトレードオフ

インデックスを減らすと WAF は下がるが、その条件で絞り込めなくなった検索は多くの行を読むことになり、読み取りアンプリフィケーション (Read Amplification) が増える。LSM ツリーのコンパクションも同じ構図で、書き込み増幅・読み取り増幅・空間増幅 (実データより多くの容量を使う度合い) の 3 つを同時に最小化することはできない。どれを削るかは、書き込み頻度・読み取り頻度・ストレージ単価のうち自分の系で一番痛いものがどれかで決める。

見積もるときは、まず論理的な書き込み量を数え、次にそれが何倍になる経路 (インデックスと GSI の更新・課金単位の切り上げ・コンパクション・ガベージコレクションの退避コピー) を 1 つずつ掛けていくとよい。増幅は掛け算で積み上がるので、一番大きい係数を 1 つ潰すだけで全体が目に見えて軽くなることが多い。

この記事は役に立ちましたか?

関連用語

関連する記事