RDB限界説の真実と現代RDSアーキテクチャの最適解
はじめに
2010年代以降、Web業界では
-
「RDBは遅い」
-
「NoSQLの方が高速」
-
「Elasticsearchを入れれば速くなる」
-
「とりあえずRedisを入れるべき」
といった考え方が広く浸透しました。
しかし、現在のハードウェア性能やRDBの内部構造を踏まえると、その多くは当時の環境や設定に起因する誤解であり、必ずしも技術的な真実ではありません。
本稿では、
-
NoSQL神話の正体
-
Elasticsearch導入の落とし穴
-
Redisキャッシュの本当のコスト
-
AerospikeやCouchbaseが普及しない理由
-
現代の大規模システムが最終的に到達している構成
について整理します。
「NoSQL/検索エンジンのインメモリ神話」の正体
2010年代前半の業界トレンド
Web2.0やビッグデータブームの中で、
RDBは古くて遅い。
これからはMongoDBやElasticsearchの時代だ。
という考え方が急速に広まりました。
しかし、その背景には大きな誤解が存在していました。
誤解の原因:MySQLデフォルト設定の罠
当時のMySQL(InnoDB)の初期設定では、
innodb_buffer_pool_size = 128MB
程度しかメモリが割り当てられていませんでした。
そのため、
-
データ件数が増える
-
インデックスがメモリに乗らない事が常時
-
SSD/HDDアクセスが頻発する事が常時
-
遅い状態がRDSの限界と認識
という状況が発生していました。
一方で、
-
MongoDB
-
Solr
-
Elasticsearch
などはインストール直後から大量のメモリを使用する設計でした。
その結果、
MySQL → 遅い
NoSQL → 速い
という印象だけが広まりました。
実際は何が起きていたのか
現代のMySQLやPostgreSQLでは、
-
Buffer Pool
-
Shared Buffer
に十分なメモリを割り当てれば、
-
インデックス
-
頻出データ
をほぼ完全にメモリ上で処理できます。
特に単純検索では、
B-Treeインデックス
の方が、
-
Elasticsearch
-
MongoDB
よりも圧倒的にメモリ効率が良く、
-
高速
-
省メモリ
-
省電力
で動作します。
つまり、
NoSQLが速かったのではなく、MySQLがまともにメモリを与えられていなかった
というのが実態です。
「システムを速くしたいからElasticsearch」の罠
現在でも、
システムが重いからElasticsearchを入れよう
という提案は頻繁に出てきます。
しかし、
全文検索要件が無い
のであれば、多くの場合で逆効果になります。
問題① データの二重管理
RDBとElasticsearchを併用すると、
MySQL
↓
同期
↓
Elasticsearch
という構成になります。
すると、
-
同期遅延
-
同期漏れ
-
データ不整合
が必ず発生します。
エンジニアは常に
なぜ検索結果とDBが違うのか
という調査に追われることになります。
問題② 運用コスト増大
Elasticsearchの実体は
Lucene(Java)
です。
そのため、
-
Heap管理
-
GC監視
-
JVMチューニング
が必要になります。
結果として、
DBを一つ増やしただけ
のつもりが、
実際には巨大なJavaシステムを一つ運用することになります。
本来やるべきこと
多くの場合、
-
インデックスがメモリ上にあるか確認
- MySQLメモリ増設
-
クエリ改善
-
適切なインデックス設計
まずはインデックスがメモリ上にあるか確認が先決です。
postgresで
SELECT
relname AS テーブル名,
heap_blks_read AS ディスクから読んだデータブロック数,
heap_blks_hit AS メモリから読んだデータブロック数
FROM pg_statio_user_tables
WHERE relname = '商品テーブル名';
「とりあえずRedisキャッシュ」の問題点
Redisは非常に優秀な技術ですが、
何でもRedisに載せるという考え方には大きな問題があります。
キャッシュミス・ペナルティ
キャッシュヒット時は高速です。
しかしキャッシュミス時には、
Redisへ問い合わせ
↓
データ無し
↓
RDSへ問い合わせ
となります。
つまり、
-
Redisアクセス
-
RDSアクセス
の二重コストが発生します。
Cache Stampede
さらに危険なのが、
-
TTL切れ
-
メモリ圧迫によるEviction
です。
高負荷時にキャッシュが一斉に失効すると、
大量のリクエストが一気にRDSへ流れ込みます。
結果として、
Redis
↓
素通り
↓
RDS直撃
となり、
RDSが一瞬で過負荷になります。
Redisのメモリ効率は良くない
Redisは
JSON
Key-Value
としてデータを保持します。
つまり、
商品情報全体を丸ごとメモリに載せることになります。
MySQLのB-Treeは極めて効率的
一方でMySQLは、
圧縮されたB-Treeインデックスを保持します。
例えば、
商品 600万件
インデックス 15本
程度なら、
インデックス全体は数GB程度で収まります。
つまり、
Redisに数十GB投資
するより、
RDSメモリ増設
の方が圧倒的に費用対効果が高いケースが多いのです。
最大の負債:キャッシュ整合性
キャッシュを導入した瞬間から、エンジニアは
キャッシュ無効化(Cache Invalidation)
という永遠の問題を抱えます。
例えば、
-
商品価格変更
-
ステータス変更
-
在庫変更
が発生した際、
Redis側を正確に消さなければ、
古いデータが表示され続けます。
この問題は想像以上に開発工数、運用工数を奪います。
Aerospike・Couchbaseが普及しきらない理由
Aerospike
Aerospikeは
インデックス=メモリ
データ=SSD
という理想的な設計です。
しかし現場では、
Redisで十分では?
という壁があります。
理由
-
Redisの採用実績が圧倒的
-
ノウハウが豊富
-
人材確保が容易
- コスト意識は二の次の現場が多い
だからです。
技術的優位性だけでは採用されません。
Couchbase
Couchbaseも優秀です。
しかし真価を発揮するには、
-
数千万件
-
数億件
-
複数ノードクラスタ
が必要になります。
問題
600万件程度では、
-
分散機能が活きない
-
固定費だけ高い
という状況になりやすいです。
600万件・15インデックスの現実
実際のメモリ消費
条件:
-
データ件数:600万件
-
インデックス:15本
理論値
インデックスやポインタを含めると、
実質数GB規模になります。
実運用
断片化や余裕領域を考慮すると、
約5〜6GB
程度になります。
さらに、
-
作業領域
-
OSキャッシュ
- 長文テキスト処理
を考慮すると、
9〜12GB
程度が必要になります。
そのため、
最低16GBクラスのRDS
が現実的な選択になります。
ただし、これは一つのテーブルでの話で通常100テーブル以上で運用する事になると常時メモリのインデックスは使えないという事になります。
「RDSは遅い」の正体
多くの人がRDSの限界だと思っている現象は、実際には
Eviction
↓
Thrashing
です。
Eviction
Buffer Poolが不足すると、
重要なインデックスが追い出されます。
Thrashing
追い出されたインデックスを、毎回SSDから再読込します。すると、
-
I/O増大
-
レイテンシ増大
-
CPU待機
が発生します。これを見たエンジニアが、
RDSが限界だ
と誤解するのです。
現代メガベンチャーが辿り着く構成
最終的に多くの大規模サービスは、
以下の思想に収束しています。
① RDSを正しく使う
-
MySQL
-
PostgreSQL
へ十分なメモリを与える。
まずはここからです。
② Textを物理分離する
商品説明などは、
別テーブルまたは別DBへ移動する。
メインテーブルは極限まで軽量化する。
③ JOINを禁止
必要なデータは、アプリケーション側で取得して結合する。これにより、
-
ロック競合減少
-
シャーディング容易化
が可能になります。
④ シャーディングで水平分割
JOIN禁止した結果、ユーザー単位やテーブル単位で物理分割しやすくなります。
これにより、サーバー追加によるスケールアウトが可能になります。
総括
「RDB限界説」の多くは、実際にはRDBそのものの限界ではなく、
-
バッファプール不足
-
長文データによるメモリ汚染
-
不適切なインデックス設計
-
キャッシュや検索エンジンの過剰導入
によって引き起こされた問題です。
高負荷システムにおける王道は、
-
長文Textを分離する
-
インデックスをBuffer Poolへ常駐させる
-
不要なRedisやElasticsearchを増やさない
-
JOIN依存を減らす
-
最後にシャーディングする
という極めてシンプルな構成です。
流行のミドルウェアを追加する前に、まずRDBのメモリ設計とデータ配置を見直すことが、最も高速で、最も安価で、最も保守性の高いアーキテクチャにつながります。
登録日:
更新日: