読み書き・空間増幅(RUM 予想)のトレードオフ
なぜ書き込みに速い DB は読み取りやディスクを犠牲にするのか。Read・Update・Memory の3増幅は同時に最小化できないという RUM 予想を軸に、各ストレージ構造の立ち位置を原理から整理します。
- 1.RUM 予想は、読み取り(Read)・更新(Update)・空間(Memory)の3つのオーバーヘッドを同時に最小化することはできず、いずれか一つは犠牲になるという経験則。アクセス手法の設計を1つの三角形の上に位置づける枠組み。
- 2.B+Tree は読み取りオーバーヘッドを小さく保つ代わりに in-place 更新で更新オーバーヘッドを払い、LSM-Tree は更新を追記で均す代わりに読み取りと空間のオーバーヘッドを払う。三角形の対角に位置する。
- 3.ブルームフィルタ・フェンスポインタ・ハイブリッド構造は、犠牲にする頂点をどれにするかを設計者が選ぶ手段。RUM はゼロサムを物理法則として宣言するのではなく、トレードオフの所在を可視化する道具として使う。
RUM 予想とは何か
データベースのストレージ構造を選ぶとき、「どれが一番速いか」という問いには答えがありません。速さの種類が複数あり、それらは互いに引き合うからです。この引き合いを1枚の地図にまとめたのが **RUM 予想(RUM Conjecture)**です。2016 年に Athanassoulis らが提唱したもので、アクセス手法(access method)が払う基本オーバーヘッドを3つに分類します。
- R(Read overhead): 求めるデータ1件を読むために、実際にアクセスする必要があるデータ量。論理1件の読み取りで何バイト・何ページ・何ファイルを触るか。
- U(Update overhead): 論理的な1更新を反映するために、実際にストレージへ加える書き込み量と移動量。
- M(Memory/space overhead): 元データに対して構造が消費する追加の空間。インデックス・冗長コピー・古い版・予約された空きなど。
RUM 予想の主張は次の一文に集約されます。「R・U・M の3つすべてを同時に最小化するアクセス手法は作れない。一つを良くすると、残りの少なくとも一つが悪化する。」三角形の3頂点に R・U・M を置き、どの構造もこの三角形の内側の1点に対応すると考えるのが、この枠組みの肝です。
LSM 文脈で語られる3つの増幅は RUM の各頂点の具体的な計測量です。読み取り増幅(read amplification)が R、書き込み増幅(write amplification)が U、空間増幅(space amplification)が M に対応します。増幅は「論理量に対する実際の物理量の比」で、RUM はその3比率が同時に1へ近づけない、と述べていると読めます。
なぜ三すくみになるのか(原理)
3つが独立に動かない理由は、読み取りを速くする仕掛けは必ず空間か更新コストで買うという物理にあります。
- 読み取りを速くしたい(R を下げる) なら、目的データへ最短で到達できる索引・整列・冗長コピーを増やす。これは追加の空間(M を上げる)を要し、その索引や整列をデータ更新のたびに維持する手間(U を上げる)も生む。
- 更新を速くしたい(U を下げる) なら、更新を「その場で書き換える」のをやめ、末尾に追記して後で整理する。すると同じキーの版が複数残り、空間が膨らみ(M を上げる)、読むときに版を横断する必要が出る(R を上げる)。
- 空間を切り詰めたい(M を下げる) なら、冗長な索引や古い版を持たず、データを高密度に詰める。すると目的データの絞り込みに使える手掛かりが減って読み取りが重くなり(R を上げる)、高密度ゆえに更新時の再配置コストも増える(U を上げる)。
つまり「速い読み取りには手掛かり(=空間か更新作業)が要る」「追記で更新を軽くすると掃除の借金が空間と読み取りに回る」という、保存則のような構造が背景にあります。
三角形の頂点に近い極端な構造
各頂点を極端に追い求めた理論上の構造を見ると、トレードオフが直感的になります。
| 近づく頂点 | 極端な構造の例 | 得るもの | 失うもの |
|---|---|---|---|
| R を最小化 | 全クエリ用に完全な索引・実体化ビューを総当たりで用意 | どの読み取りも一発で到達 | 索引維持で U が爆発、コピーで M が爆発 |
| U を最小化 | 純粋なログ(追記のみ、整理しない) | 1更新=1追記で最速 | 読むたび全ログ走査で R 最大、版が残り M 最大 |
| M を最小化 | 索引なしのヒープを隙間なく詰める | 余分な空間ゼロ | 全件スキャンで R 最大、再配置で U 増 |
現実のエンジンは、この3頂点のどれか1つに振り切らず、三角形の内部のどこかへ意図的に配置されています。設計とは、どの頂点を犠牲にするかの選択です。
実在のストレージ構造を地図に置く
代表的な構造が RUM 三角形のどこに位置するかを整理します。点読み取り(単一キー検索)の挙動を基準にしています。
| 構造 | R(読み取り) | U(更新) | M(空間) | 性格 |
|---|---|---|---|---|
| B+Tree | 小(根→葉の1経路で安定) | 中(対象ページを探し in-place 更新、分割あり) | 中(ノード内の空きで断片化) | 読み取り寄り。OLTP の既定解 |
| LSM-Tree | 中〜大(複数 SSTable を横断) | 小(追記+シーケンシャル書き出し) | 大(古い版・トゥームストーンが残る) | 更新寄り。書き込み主体・時系列 |
| ハッシュ索引 | 小(点読み取りは平均 O(1)) | 中(再ハッシュ・衝突連鎖の維持) | 中〜大(負荷率を下げる余裕を確保) | 点読み取り特化、範囲検索は不可 |
| 素のヒープ(索引なし) | 大(全件スキャン) | 小(末尾追記) | 小(追加構造を持たない) | M に振り切った極端 |
| カラム志向+強圧縮 | 条件次第(射影は速いが点検索は弱い) | 大(圧縮ブロック再構築) | 小(高圧縮で M を削る) | M 寄り、分析向け |
B+Tree と LSM-Tree は三角形のほぼ対角に位置するのが要点です。B+Tree は読み取りを根から葉への1経路に固定して R を小さく保つ代わりに、更新を in-place で行うため U を払います。LSM-Tree は更新を追記に変えて U を均す代わりに、版が複数ファイルへ散らばって R が増え、古い版とトゥームストーンが残って M が膨らみます。この対比は B+Tree の内部構造を更新方式の側から見直すと一段はっきりします。
設計レバーは「犠牲にする頂点を選ぶ手段」
RUM の価値は、最適化テクニックを「どの頂点の借金を、どの頂点へ付け替えるか」として読み解けることにあります。
- ブルームフィルタ: わずかな空間(M を少し上げる)を払って、存在しないキーへの無駄なファイル読み取りを排除し R を下げる。M↑ で R↓ という付け替え。詳しくはブルームフィルタ。
- フェンスポインタ/スパース索引: ソート済みファイルにキーとオフセットの粗い対応表を持たせ(M を少し上げ)、目的位置へ直接ジャンプして R を下げる。
- コンパクション戦略: LSM のコンパクション戦略は、まさに R・U・M のどこへ重みを置くかのダイヤルそのものです。Leveled は統合を頻繁に行って R と M を抑える代わりに U(書き込み増幅)を払い、Size-Tiered は統合を遅らせて U を抑える代わりに R・M を払います。
- 負荷率(load factor)チューニング: ハッシュ索引で空きを多く確保すれば衝突が減って R↓・U↓ になるが、その分 M↑。空間を読み書きへ両替している。
これらに共通するのは、RUM 三角形の外側へは出られないという前提です。どのレバーも、ある頂点のコストを別の頂点へ移すだけで、3頂点の合計的な「楽さ」を一方的に増やすことはできません。
in-place 更新と追記更新で、削除の「借金」がどこへ回るかを並べます。
# B+Tree 系: その場で消す(U を払い、M・R は増やさない)
delete(K):
leaf = traverse_from_root(K) # R 相当のコスト
remove_entry(leaf, K) # ページ書き戻し = U
if underflow(leaf): merge_or_rebalance(leaf) # 追加の U
# LSM 系: 消さずにトゥームストーンを追記(U を軽くし、M・R へ付け替え)
delete(K):
append(memtable, tombstone(K)) # 1 追記 = 小さな U
# 実体の K はまだ古い SSTable に残る → M が増える
# 読むときトゥームストーンに当たるまで横断 → R が増える
# 借金はコンパクション時にまとめて返済される(その時 U を払う)
RUM をどう使うか(限界も含めて)
RUM は厳密な数学的定理ではなく **予想(conjecture)**です。任意の構造の R・U・M に普遍的な下限を与える証明ではなく、「3つを同時に底まで下げる手法は知られておらず、設計上ほぼ常にトレードオフが現れる」という強い経験則として使うのが正しい姿勢です。実務では次のように働かせます。
- ワークロードの偏りを R/U/M のどれが支配するかへ翻訳する。 読み取り主体の OLTP なら R を守る(B+Tree 寄り)、書き込み洪水なら U を守る(LSM 寄り)、コスト最優先のアーカイブなら M を守る、という対応づけ。
- 「速くする」提案を必ず付け替えとして疑う。 ある索引を足して読み取りを速くする話には、必ず U と M の請求書が付いています。請求先を確認しないチューニングは別の頂点を悪化させます。
- ハイブリッドは三角形の内部移動。 多くの現代エンジンは LSM に B+Tree 的な索引やキャッシュを足すなどして三角形の中心寄りに置きますが、これは魔法ではなく中庸を選んだという宣言です。
RUM 予想は Read・Update・Memory のオーバーヘッドを同時に最小化できないという主張で、R=読み取り増幅・U=書き込み増幅・M=空間増幅に対応する、と即答できるように。B+Tree は R を、LSM-Tree は U を優先し三角形の対角に位置すること、ブルームフィルタは M を少し払って R を下げる付け替えであることがセットで問われます。「定理ではなく予想/経験則」である点も押さえると差がつきます。
まとめ
RUM 予想は、アクセス手法が払うオーバーヘッドを R(読み取り)・U(更新)・M(空間)の三角形に置き、3つ同時の最小化は不可能と宣言する枠組みです。読み取りを速くする手掛かりは空間か更新作業で買うしかない、という保存則的な構造が背景にあります。B+Tree は R 寄り、LSM-Tree は U 寄りで対角に位置し、ブルームフィルタやコンパクション戦略は「犠牲にする頂点を選ぶレバー」として読めます。チューニングを 頂点間の借金の付け替えと捉えれば、どの最適化にも必ず請求先があると分かります。
データベース Article
読み書き・空間増幅(RUM 予想)のトレードオフを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
RUM予想
比較で見る軸
難易度: advanced / カテゴリ: データベース / タグ数: 6
導入後に効く点
B+Tree は読み取りオーバーヘッドを小さく保つ代わりに in-place 更新で更新オーバーヘッドを払い、LSM-Tree は更新を追記で均す代わりに読み取りと空間のオーバーヘッドを払う。三角形の対角に位置する。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- データベース
- タグ数
- 6
判断チェックリスト
- 自社の用途が「RUM予想 / 増幅」に近いか確認する。
- 強みである「RUM 予想は、読み取り(Read)・更新(Update)・空間(Memory)の3つのオーバーヘッドを同時に最小化することはできず、いずれか一つは犠牲になるという経験則。アクセス手法の設計を1つの三角形の上に位置づける枠組み。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。