MySQL FULLTEXTが遅い理由
データ量や同時アクセスが増えると、MySQL FULLTEXTの検索が実用上のボトルネックになることがあります。その原因と解決策を解説します。
何が問題なのか
ディスクI/Oがボトルネック
MySQL FULLTEXTはインデックスをディスク上のB-treeに保存しています。検索のたびに:
- ディスクからB-treeページを読み込み
- ポスティングリストをメモリに展開
- 結果のソート(大抵は外部ソート)
SSDを使っていても、このI/Oオーバーヘッドが大きくなり、ベンチマークではクエリによって数百msから数秒かかりました。
用語補足
ポスティングリストは「この語を含むドキュメントIDの一覧」です。全文検索は、この一覧同士をAND/ORで組み合わせて候補を探します。詳しくは用語集を参照してください。
ポスティングリストが非圧縮
MySQLはポスティングリストを圧縮しません。「の」「を」のような頻出語で数百万件ヒットすると:
- クエリごとに数MBのデータを読み込む
- バッファプールを圧迫
- 並列アクセスでキャッシュが効かなくなる
キャッシュ頼み
キャッシュの状態で性能が大きく変わります。110万件のWikipedia記事でのベンチマークでは、MySQL FULLTEXTのレイテンシはクエリとキャッシュ状態に応じて300ms〜2,566msの範囲でした。
本番環境では再起動・デプロイ・メモリ競合でキャッシュが効かない状況が頻発します。
並列アクセスで伸びにくい
並列アクセスでは、MySQL FULLTEXTのスループットが伸びにくくなることがあります:
| ソリューション | 並列接続数 | QPS |
|---|---|---|
| MySQL FULLTEXT | 1-4 | 2-8 |
| MygramDB | 1-4 | 2,634-11,766 |
MySQL FULLTEXTは低い並列数でも一桁台のQPSにとどまり、MygramDBは数千QPSを維持します。
解決策:MygramDB
MygramDBはアーキテクチャから見直すことでこれらの問題を解決しています。
検索インデックスをメモリ上に保持
検索インデックスはRAM上に保持されます。MySQL FULLTEXTのように、検索のたびにディスク上の全文検索インデックスへ戻る構成ではありません。
圧縮されたポスティングリスト
デルタエンコーディングとRoaringビットマップで、ポスティングリストを圧縮して保持します。頻度の低い語と高い語で表現を切り替えることで、メモリ使用量と検索速度のバランスを取ります。
SIMD命令で高速化
ビットマップ演算にCPUのSIMD命令を活用し、スループットを最大化。
キャッシュ状態に左右されにくい性能
インデックスをメモリ上に持つため、検索のたびにディスクI/Oへ戻りにくい構成です。ベンチマークでは主要な英語クエリで0.08-0.42msの応答でした。詳しい数値はベンチマークをご覧ください。
MySQL / MariaDBのbinlogに追従
GTIDベースのbinlogレプリケーションで、MySQL 8.4/9.xまたはMariaDB 10.6+/11.xの変更に追従します。検索用のETLパイプラインは不要です。反映遅延は通常小さくなりますが、MySQL側の負荷、ネットワーク、MygramDBの処理状況によって変動します。
用語補足
ETL はデータを抽出(Extract)し、変換(Transform)し、別システムへ投入(Load)する仕組みです。MygramDBはMySQLのbinlogを直接読むため、検索用の同期ジョブを別途作る必要がありません。
向いているケース
- 高トラフィックなWebサービス — ECサイト、ニュースサイト、Q&Aサービスなど同時アクセスが多い環境
- リアルタイム検索が必要 — ユーザーが即座に結果を期待する場面
- FULLTEXTが100ms超 — 検索の遅さがUXを損なっている
- 負荷時にタイムアウト — アクセス集中で検索が落ちる