メインコンテンツへスキップ
  1. Posts/

なぜNoSQLとElasticsearchは特定のワークロードで有利なのか? LSMツリーとI/Oまで

NineKoo9
著者
NineKoo9
目次

このドキュメントは、疑問を持っていた点をAIエージェントと対話しながら整理したメモです。NoSQLとElasticsearchが特定のワークロードで有利になる理由を、LSMツリー、転置インデックス、HDD・SSD I/Oの観点から整理します。

なぜNoSQLとElasticsearchは特定のワークロードで有利なのか? LSMツリーとI/Oまで
#

1. NoSQLはなぜRDBMSより速いのか?
#

「NoSQLはRDBMSより速い」という言葉は条件付きでのみ正しい。
製品ごとにデータモデル、ストレージエンジン、一貫性モデル、分散構造が異なるからだ。
そのため以下の項目は「一部のNoSQLが特定のワークロードで有利になりうる理由」として理解する方が正確だ。

1-1. ジョインがない(最大の理由の1つ)
#

RDBMSは正規化されたテーブルを実行時にジョインしてデータを組み合わせる。

SELECT u.name, o.total, p.name
FROM Users u JOIN Orders o ON ... JOIN Products p ON ...

Document DBでは関連データを1つのドキュメントに埋め込むモデルを選択できる。

{
  "user": "建弘",
  "orders": [{ "total": 5000, "product": "本" }]
}

Mongodbのドキュメントでは、埋め込みデータモデルが関連データを1回のデータベース操作で取得できるようにすることがあり、読み取りパフォーマンスの優位性がある可能性があると説明している。
ただしこれはMongoDBのすべてのモデルが自動的に速いという意味ではなく、埋め込みを選択したモデリング方式が読み取りパスを単純にできるという意味だ。

1-2. スキーマがないから書き込みが速い
#

この表現はそのままにしておくと断定的すぎる。

MongoDBのドキュメントは基本的に柔軟なスキーマモデルを使用し、同じコレクションのドキュメントが基本的に同じフィールドとデータ型を持つ必要はないと説明している。同時にschema validationで制約を与えることもできると説明している。

つまりより正確な表現は以下に近い。

  • 一部のドキュメントDBはデフォルトスキーマがより柔軟だ。
  • しかし「スキーマがないから無条件に書き込みが速い」とは一般化できない。
  • 実際の書き込みコストは検証ルール、インデックス数、レプリケーション、durability設定などの要素にも大きく左右される。

1-3. 水平スケール(シャーディング)が設計に組み込まれている
#

これも製品別の違いを見る必要がある。

Apache Cassandraのドキュメントは、Cassandraがconsistent hashingベースのdataset partitioningmulti-master replicationincremental scale-outを使用すると説明している。このようなシステムは分散とパーティショニングがアーキテクチャの中心にあるため、ノードの追加がより自然だ。

ただしこれを**「ノードを増やせばスループットが常に線形増加する」**と理解するのは過剰だ。
実際のパフォーマンスはパーティションキーの分布、レプリカ数、ネットワーク、クエリパターンによって変わる。

1-4. ACIDを諦めた代償
#

この文章も範囲が広すぎる。

一部の分散NoSQLシステムはリレーショナルDBと異なる一貫性/トランザクションモデルを採用する。例えばCassandraはtunable consistencylast-write-winsベースの競合解決を説明しており、cross-partition transactionをリレーショナルDBのように一般的なデフォルトモデルとして置いていない。

しかしすべてのNoSQLがACIDを「諦める」と書くのは不正確だ。
そのためこの項目は以下のように理解する方が正しい。

  • 一部のNoSQLは強い一貫性や汎用トランザクションよりも可用性、分散性、シンプルな書き込みパスの方に重きを置く。
  • その選択が特定のワークロードでのパフォーマンス優位性につながりうる。

1-5. ディスク保存方式(Column-family DB限定)
#

元文でColumn DB(Cassandra、HBase)と1まとめにした部分は少し注意が必要だ。
Cassandraは分析用columnar DBというよりwide-column / column-familyの系統と見る方がより正確だ。

区分典型的な行中心保存Column-family系の一部のアプローチ
保存/アクセスの観点行全体中心パーティション・カラムグループ中心
特化する状況行単位の照会/更新モデリングされたアクセスパターンに合ったキーベースの照会

つまりこの項目の核心は「すべてのNoSQLがカラム保存だから速い」ではなく、データモデルと保存構造がアクセスパターンによりよく合いうるという点だ。

1-6. LSMツリー(書き込みパフォーマンスの核心)
#

→ 2節で詳細説明

要約比較表
#

理由核心的な効果
埋め込みベースのモデリング可能読み取りパスの単純化が可能
LSM系ストレージエンジン小さな書き込みをappend/flushベースで処理しやすい
分散構造を内蔵した製品が存在パーティショニングと増設がより自然
一貫性/トランザクションモデルの違い一部のワークロードでオーバーヘッド削減可能
データモデルの最適化アクセスパターンによりよく合いうる

注意: 複雑なジョイン、強い一貫性、汎用トランザクションが重要な場合は、RDBMSの方がより適切またはより速い場合がある。


2. LSMツリー(Log-Structured Merge Tree)
#

2-1. 核心アイデア
#

小さなランダム更新をメモリにまとめてから、ソートされたファイルに書き出す構造

Bツリー系ストレージエンジンは論理的には既存のページを探して更新するモデルに近い。
LSM系は小さな書き込みをメモリにまとめておいてから新しいsorted fileを作ってflush/mergeする方に近い。

このときオペレーティングシステムのpage cacheやストレージコントローラーが実際のI/Oを緩衝できるため、
「Bツリー = 常に物理ランダム書き込み」と断定するのは過剰だ。
ただしストレージエンジンの
論理的な書き込みパターン
が異なるという点は明らかだ。

2-2. 書き込みフロー ステップ別説明
#

LSMツリー書き込みフロー
書き込みリクエスト
① MemTable(メモリにソートされた構造で積み上げる)
② SSTable(ディスクに新しいsorted fileとしてflush)
③ Compaction(複数のSSTableをマージ・整理)

① MemTable(メモリフェーズ)
#

  • RocksDBのドキュメント基準で新しい書き込みはまずmemtableに入る。
  • readsもmemtableを最初に参照する。最新のデータがここにあるからだ。
  • memtableがいっぱいになるとそれ以上書き込まないimmutable memtableになる。

② SSTable(ディスクフェーズ)
#

  • バックグラウンド処理がimmutable memtableをSSTファイルにflushする。
  • Cassandraのドキュメントもmemtableが限界に達するとディスクにflushされてimmutable SSTableになると説明している。
  • 核心は既存のファイルをその場で修正するよりも新しいソートファイルを作って書き出す構造という点だ。

③ Compaction(バックグラウンド整理)
#

  • 複数のSSTableが積み重なるとcompactionがこれをマージする。
  • RocksDBのドキュメントはcompactionがどのsorted runをマージするか、そして読み取り時にどのsorted runを参照しなければならないかに直接影響を与えると説明している。
  • つまりcompactionは単純な掃除ではなくLSMの読み取り/書き込みのトレードオフを管理する核心的な動作だ。

2-3. なぜ順次書き込みが有利なのか?
#

ストレージデバイス核心の違い意味
HDDヘッド移動と回転遅延が大きい順次アクセスが特に有利
SSDヘッドはないがerase/write単位とGCコストがある順次パターンがwrite amplificationを減らしやすい

HDDでは連続した区間を読み書きする方が物理的に有利だ。
SSDでは理由が異なる。Samsungの資料はNAND SSDがerase-before-write特性を持ち、garbage collectionによるwrite amplificationが発生すると説明している。またhostのwriteパターンがsequentialのときSSD WAFが1に近くなり、randomパターンはWAFを高める可能性があると説明している。

2-4. LSMツリーを使う代表的な例
#

システム備考
Cassandra公式ドキュメントでストレージエンジンがLSMベースだと説明
RocksDBmemtable → SST → compaction構造を公式Wikiで説明

元文にあったMongoDB、HBase、LevelDBまで一行で同じカテゴリにまとめて書く方式は説明が雑になるため、ここでは今回の検証で直接確認したシステムのみ記載する。

2-5. LSMツリーの欠点
#

書き込みが速い代わりに読み取りとバックグラウンド整理のコストが伴う。

  • Cassandraのドキュメントは、LSMベースのwrite pathがwrite-orientedなワークロードに最適化されているが、read performanceとwrite amplificationのトレードオフがあると説明している。
  • CassandraはRead性能補完のためにBloom filterを使用すると説明している。
  • compaction自体も複数のSSTableを再度読んで新たに書き出す必要があるため、バックグラウンドI/Oが発生する。

2-6. 一言まとめ
#

「その場で継続的に修正するよりも、メモリにまとめてから新しいsorted fileとして書き出して後でマージする」という戦略がLSMの核心だ。


3. Elasticsearchはなぜ検索が速いのか?
#

3-1. 転置インデックス(Inverted Index)— 核心
#

Elasticsearch検索速度 — 転置インデックス / シャード / セグメント

一般的な行スキャン方式では"Java"を探すために文書を1つずつ確認しなければならない。

doc1: "Java Spring 開発者募集"
doc2: "Python データエンジニア募集"
doc3: "Javaバックエンド 3年以上"

転置インデックスは逆に用語 → 文書リストの構造を作る。

"Java"   → [doc1, doc3]
"Spring" → [doc1]
"Python" → [doc2]

そのため検索時に全文書を最初から最後まで走査する代わりに、
該当する用語のpostings listに直接アクセスできる。
つまり核心は全数スキャンを避けるインデックス構造にある。

3-2. シャード(Shard)並列検索
#

インデックスを複数のシャードに分けると検索処理を分散できる。

検索リクエスト
    ├──→ Shard 0
    ├──→ Shard 1
    └──→ Shard 2
         結果マージ → レスポンス

ただし元文のように**「シャードが3つなら検索時間が理論上1/3」と言うのは過剰だ。
Elasticのドキュメントは
各シャードが検索をsingle CPU threadで実行すると説明しつつ、同時にシャードが多すぎると検索パフォーマンスが低下することがある**とも説明している。

つまりより正確な表現は以下のとおりだ。

  • シャードは並列処理の単位になりうる。
  • しかしシャード数を増やせば無条件に速くなるわけではない。
  • 実際のshard sizeと数はベンチマークで調整する必要がある。

3-3. セグメント(Segment)— LSMと似ている部分
#

各シャード内部はLuceneセグメントで構成される。

操作処理方式
追加新しいセグメントとして反映
削除即時物理削除ではなく、その後のmergeで整理
修正内部的には削除 + 新しいドキュメント追加に近い処理

Elasticのドキュメントは、Elasticsearchがnear real-time searchを提供し、ドキュメントがインメモリのインデックシングバッファから新しいセグメントとして記録され、このセグメントがまずfilesystem cacheに載ってから検索可能になると説明している。このプロセスをrefreshと呼ぶ。

またElasticのドキュメントはセグメントがimmutableであり、小さなセグメントが後でより大きなセグメントにmergeされると説明している。

したがってElasticsearchは検索エンジンの観点から以下のように理解すればよい。

  • 検索可能なセグメントを素早く開く。
  • 削除と整理はmergeがバックグラウンドで担当する。
  • おかげで検索パスとインデックシングパスを比較的分離して運営できる。

3-4. まとめ
#

Elasticsearchが速い理由は転置インデックス + シャード単位分散 + immutableセグメントとrefresh/mergeモデルが一緒に噛み合うからだ。

さらにElasticは以下も公式に提供している。

  • textフィールド用analyzer
  • デフォルト類似度BM25
  • Levenshtein edit distanceベースのfuzzyクエリ

つまりElasticsearchは単純なインデックスライブラリではなく、検索機能のサーフェスそのものが広い分散検索エンジンだ。


4. MySQL全文検索 vs Elasticsearch
#

質問: MySQLも全文検索(Full-Text Search)に転置インデックスを使っているのに、なぜElasticsearchより遅かったり不便に感じることがあるのか?

核心は転置インデックスの有無自体ではなく、どんな問題を主目的として設計しているかだ。

4-1. シャーディングなし — 単一ノード
#

元文のように「MySQLは基本的に1台のサーバー内で一人ですべて処理する」と単純化するのは雑すぎる。
アプリケーションレベルのシャーディングや他の分散構成が可能だからだ。

ただしMySQLのFULLTEXT検索は本質的にトランザクションDB内の機能であり、
Elasticsearchはシャード分散を基本概念とした検索エンジンであるという違いは大きい。

つまり大規模分散検索という問題では、Elasticsearch側がより直接的な解法を提供する。

4-2. 転置インデックスの品質が異なる
#

ここで最も重要な訂正が必要だ。

MySQL InnoDB full-text indexも公式ドキュメント上はinverted index designだ。
またMySQLはrelevance計算、boolean search、query expansion、CJK用のbuilt-in ngram parserも提供している。

したがって違いは「MySQLは転置インデックスがなくElasticsearchだけにある」ではない。
より正確な違いはテキスト分析と検索機能サーフェスの幅にある。

機能MySQLElasticsearch
転置インデックスサポート(FULLTEXTサポート
関連度計算サポートサポート
CJK処理ngram parserサポートanalyzerの組み合わせで豊富に設定可能
タイポ許容限定的fuzzy query提供
アナライザー構成比較的限定的analyzer中心で非常に柔軟

4-3. 書き込み/読み取りの競合(構造の違い)
#

MySQL側はInnoDB full-text indexを維持するためにauxiliary index tablesfull-text index cacheを使用し、最近挿入されたトークンをキャッシュにまとめてからbatch flushする。

Elasticsearch側はrefreshで新しいセグメントを開き、その後mergeで整理する

したがって違いはこのように理解する方が良い。

  • MySQL全文検索はトランザクションDB内部に統合された検索機能だ。
  • Elasticsearchは検索を中心にセグメント/refresh/mergeモデルを運営する。

つまり両方ともインデックスを維持するが、運用モデルが異なる。

4-4. メモリ活用戦略が異なる
#

この項目も元文の表現を調整する必要がある。

  • Elasticsearchは公式ドキュメントでfilesystem cacheの上でセグメントを素早く開いて検索可能にするnear real-timeモデルを説明している。
  • MySQL InnoDBは公式ドキュメントでfull-text index cacheを設けて最近挿入したトークンを一時保存してbatch flushすると説明している。

つまり両方ともキャッシュを活用するが、何を、どんな目的で、どんな検索構造に合わせてキャッシュするかが異なる。

4-5. 結論
#

転置インデックスというアイデア自体は両方に存在する。
違いはトランザクションDBに含まれた全文検索機能なのか、
それとも分散検索を主目的として作られた検索エンジンなのかにある。

状況より自然な選択
リレーショナルデータ内での比較的シンプルな検索MySQL全文検索
分散検索、analyzer調整、fuzzy search、検索専用運用Elasticsearch

5. ランダムI/O vs 順次I/O — ハードウェアまで
#

質問: 結局OS観点ではファイルを開くことなのに、なぜ速度差が生まれるのか?
質問: BツリーとLSM Treeの違いがハードウェアレベルでどのように適用されるのか?

5-1. I/Oソフトウェアスタック — レイヤー全体の流れ
#

I/Oソフトウェアスタック — Appからハードウェアまで

file.write(data)を呼び出したときの流れを単純化すると以下のとおりだ。

アプリケーション(Bツリー / LSMツリー)
OSページキャッシュ
ファイルシステム(ext4 / xfsなど)
ブロック層 / デバイスドライバー
HDD / SSD

Linuxカーネルのドキュメントはnormal reads, writes and mmaps go through the page cacheと説明している。
またext4のドキュメントは構造の位置が絶対ディスクオフセットではなくblock number基準で保存されると説明している。

したがって核心は「ファイルを開く」という事実自体よりも、
ストレージエンジンがどんな論理的な書き込みパターンを作り、ファイルシステムとデバイスがそれをどんなブロック配置で実現するかにある。

5-2. HDDハードウェア — ランダムvs順次の物理的な違い
#

HDDランダムI/O vs 順次I/O — ヘッド移動比較

HDDのパフォーマンスは大まかに以下のコストで説明できる。

要素説明
Seek timeヘッドが目的のトラックに移動Seagateの例で平均<12ms
Rotational latency目的のセクターがヘッドの下に来るまで待機Seagateの例で平均5.1ms
Transfer time実際にデータを読み書きする時間ブロックサイズ・転送速度によって異なる

核心は小さなランダムI/Oでは実際の転送時間よりも位置を探しに行くコストの方がはるかに大きいという点だ。

  • ランダムI/O: seek + 回転待機が複数回繰り返される
  • 順次I/O: 一度位置を確定してから連続転送しやすい

そのためHDDでは順次パターンの優位性が特に顕著に現れる。

5-3. Bツリー vs LSMツリー — ブロックアクセスパターン比較
#

Bツリー vs LSMツリー ブロックアクセスパターン / SSD Write Amplification

Bツリーの書き込み(既存ページ更新中心)
#

① 関連ページを探索
② メモリで更新
③ 変更ページを再度書き込んで反映
  • 論理的には既存の位置のページを探して更新するモデルに近い。
  • そのため小さな更新が多くなるほど、ストレージ層に散らばった書き込み負荷を作りやすい。

ただしこの文章を**「Bツリーは無条件に毎回物理ランダム書き込み」**と理解してはいけない。
page cache、filesystem allocation、SSD FTLが中間で緩衝して再配置できるからだ。

LSMツリーの書き込み(新しいファイルのappend/flush中心)
#

① メモリのMemTableにまず反映
② いっぱいになったらSSTableにflush
③ 後でcompactionによりマージ
  • 論理的には新しいsorted fileを作って書き出す構造だ。
  • そのため小さな書き込みをより大きな単位のflush/merge作業にまとめやすい。
  • このパターンがHDDの順次I/OやSSDのwrite amplification管理によりよく合う場合が多い。

SSDでも順次が有利な理由
#

項目内容
Erase-before-writeNANDはoverwriteを直接行わず消去-書き込み特性を持つ
Garbage collection有効ページを移動しながらblockを空ける過程が必要
Write AmplificationGCのためhostのwriteよりも多くの内部writeが発生しうる
順次パターンの優位性Samsungの資料基準でsequentialなhost writeはSSD WAFを1に近づけられる

つまりSSDではHDDのようにヘッド移動がボトルネックにはならないが、
GCとWAFのため書き込みパターンが依然として重要だ。

5-4. 一言まとめ
#

Bツリー系は論理的に既存ページを更新する構造に近く、
LSM系は新しいsorted fileを作って書き出す構造に近い。
実際の物理I/Oはカーネルとデバイスが緩衝するが、ストレージエンジンの書き込みパターンの違いはパフォーマンス特性として現れる。


全体概念の連結マップ
#

NoSQLが特定のワークロードで有利になる理由
├── 読み取り側面
│   ├── 埋め込みベースのモデリング(一部のDocument DB)
│   ├── 転置インデックス(Elasticsearch、MySQL FTS)
│   ├── シャード単位分散検索(Elasticsearch)
│   └── immutableセグメント + refresh/merge(Elasticsearch)
└── 書き込み側面
    ├── LSMツリー → memtable + SSTable + compaction
    ├── 分散製品別consistency/transactionモデルの違い
    └── 柔軟なスキーマ/モデリングの選択肢(一部のDocument DB)

ランダム vs 順次I/O
├── OSレイヤー: page cacheを経由
├── ファイルシステム: block number基準で構造を解釈
├── HDD: seek + rotational latencyが大きなコスト
└── SSD: garbage collection + write amplificationが核心コスト

Bツリー vs LSMツリー(I/O観点)
├── Bツリー: 既存ページ更新中心
└── LSMツリー: 新しいsorted file flush/merge中心

参考資料
#