Skip to content

よくある質問

MySQL FULLTEXTの問題

MySQL FULLTEXTが遅いのはなぜですか?

ディスクI/O、非圧縮の転置リスト、キャッシュ依存、並列アクセスの破綻が原因です。詳しくはMySQL FULLTEXTが遅い理由をご覧ください。

解決策: MygramDBは全データをメモリに保持し、常にサブミリ秒のレスポンスを実現。数値はベンチマークを参照。

MySQL FULLTEXTが負荷時にタイムアウトします。どう対処すべきですか?

MygramDBを検索レプリカとして追加してください。110万件のWikipedia記事でのベンチマークでは、MySQL FULLTEXTが2-8 QPSにとどまるのに対し、MygramDBは2,634-11,766 QPSを達成します。

アプリケーションを変更せずにMySQL FULLTEXTを高速化できますか?

はい。MygramDBはbinlogレプリケーションでMySQLと同期します。アプリケーションは通常通りMySQLに書き込み、検索クエリだけをMygramDBにルーティングします。セットアップはクイックスタートをご覧ください。

他の選択肢との比較

Elasticsearchの代わりにMygramDBを使うべきですか?

詳しくは比較ページをご覧ください。端的に言えば、データがRAMに収まりMySQL/MariaDBとのシンプルな連携が必要ならMygramDB。複数ノードにまたがる分散検索、ペタバイト規模のデータ、より高度な分析・集計機能が必要ならElasticsearch。

MeilisearchやTypesenseとの違いは?

MeilisearchやTypesenseは、タイポ補正・ファセット検索・管理UIを備えた汎用検索エンジンです。MygramDBは異なる問題を解決します。MySQLからbinlogで直接同期するインメモリ検索レプリカです。データが既にMySQLにあり、ETLパイプラインなしで高速な全文検索が必要な場合はMygramDBの方がシンプルです。独立した検索プラットフォームとして独自のデータ取り込みが必要な場合は、MeilisearchやTypesenseが適しています。

RediSearchとの違いは?

どちらもインメモリの全文検索エンジンですが、アーキテクチャが根本的に異なります。

MygramDBRediSearch
データ同期MySQL/MariaDB binlogで自動同期プッシュ型(FT.ADD)— ETLは自前で構築
依存MySQLまたはMariaDBRedis(Redis Stack)
デプロイ単一バイナリRedisモジュール
機能N-gram検索+BM25スコア、ハイライト、ファジー検索、ファセット集計、シノニム展開ファジー検索、ハイライト、集計、地理
ライセンスMITRSALv2 / SSPL(商用制限あり)

最大の違いはデータフローです。RediSearchはドキュメントをRedisにプッシュする必要があり、MySQLとRedis間のETLパイプラインが必要です。MygramDBはMySQL/MariaDBのbinlogを直接読み取り、自動で同期を維持します。データがMySQL/MariaDBにあり、同期パイプラインを構築せずに検索したい場合はMygramDBの方がシンプルです。既にRedisを運用しており、地理検索やより豊富な集計モデルが必要な場合は、RediSearchの統合コストに見合う可能性があります。

機能と制約

MygramDBに必要なメモリ量は?

verify_textモードによります。verify_text: off(デフォルト)では100万ドキュメントあたり約813MB(インデックスのみ)。verify_text: all(テキスト検証有効)では100万ドキュメントあたり約2.3GB(ポストフィルタ用の正規化テキストを含む)。1000万ドキュメントなら約8GB(off)または約23GB(all)を想定してください。

MygramDBは日本語に対応していますか?

はい。ICUベースのUnicode正規化とN-gramトークナイズを使用。日本語・中国語・韓国語は追加プラグインなしでそのまま動作します。

ファジー検索(タイポ補正)には対応していますか?

はい(v1.6.0以降)。クエリに FUZZY(または FUZZY 2)を追加すると、Levenshtein編集距離1または2以内の語句にマッチします。候補は長さの差で事前フィルタされるため、オーバーヘッドは抑えられています。詳しくはクエリガイド — ファジー検索を参照してください。

検索結果のハイライトには対応していますか?

はい(v1.6.0以降)。HIGHLIGHT 句を追加すると、設定可能なタグ(TAG)、フラグメント長(SNIPPET_LEN)、フラグメント数(MAX_FRAGMENTS)でスニペットが返却されます。スニペットは保存済みの正規化テキストから切り出されるため、verify_text"ascii" または "all" に設定する必要があります。詳しくはクエリガイド — ハイライトを参照してください。

関連度順のランキングには対応していますか?

はい(v1.6.0以降)。SORT _score DESC を使うとBM25関連度スコア(k1=1.2b=0.75)で並び替えられます。IDF/TFはクエリ時に計算されるため、verify_text"ascii" または "all" に設定する必要があります。

水平スケールは可能ですか?

組み込みのスケール機能はありません。MygramDBは設計上シングルノードです。単一ノードのRAMを超えるデータセットの場合:

  • ロードバランサーの背後に複数のMygramDBインスタンスをデプロイ
  • 各インスタンスが同じMySQL Primaryから全データをレプリケーション
  • 真のシャーディングにはID範囲でテーブルを分割し、アプリケーション層でクエリをルーティング

多くのユースケース(64〜128GBサーバーで最大約5000万ドキュメント)では、単一ノードで十分です。

複数テーブルを横断検索できますか?

できません。各クエリは単一テーブルを対象とします:SEARCH articles hello world。MygramDBは複数テーブルの設定に対応していますが、各テーブルは独立したインデックスとドキュメントストアを持ちます。クロステーブル検索はアプリケーション層で個別クエリとして処理してください。

MygramDBはPostgreSQLに対応していますか?

非対応です。MygramDBはMySQL固有のバイナリログレプリケーションプロトコルに依存しています。PostgreSQLは異なる仕組み(WALベースのロジカルレプリケーション)を使用しています。現時点でPostgreSQL対応の予定はありません。

MariaDBで動作しますか?

はい(v1.6.0以降)。MygramDBはMariaDB 10.6以降および11.xに対応し、MariaDBネイティブのGTID形式(domain-server-sequence)でレプリケーションします。SELECT VERSION() からサーバー種別を自動判定するため、同じ mysql セクションの設定でMySQL 8.4/9.xとMariaDBを切り替えられます。E2EテストはMariaDB 10.11および11.4で行っています。

インデックスと検索

なぜ形態素解析ではなくN-gramですか?

N-gramトークナイズは言語に依存しません。形態素解析器(MeCab、kuromoji、jieba)は辞書・言語判定・バージョン管理が必要です。N-gramは英語・日本語・中国語・韓国語・多言語混在コンテンツで一律に動作します。

MygramDBはハイブリッド方式を採用しています。ASCII/英数字にはバイグラム(サイズ2)、CJK文字にはユニグラム(サイズ1)を使用。どちらもテーブル単位でngram_sizekanji_ngram_sizeで設定可能です。

Roaring Bitmapはどう使われていますか?

転置リストは密度に応じたハイブリッドストレージ戦略を採用しています:

  • 疎な転置リスト(全ドキュメントの18%未満): デルタエンコーディング配列。DocIDを前の値との差分で保存し、4バイト整数を1〜2バイトに圧縮。
  • 密な転置リスト(18%以上): CRoaringによるRoaring Bitmap。65,536 ID単位のチャンクごとに最適なコンテナタイプ(配列・ビットマップ・ランレングス)を自動選択。

閾値はroaring_thresholdで設定可能。フォーマット間の振動を防ぐヒステリシス係数(0.5倍)があります。

「SIMD高速化」とは具体的に何ですか?

MygramDBはCRoaringをリンクしており、CRoaringがビットマップ演算(AND、OR、カーディナリティ)で内部的にSIMD命令を使用します。x86-64ではAVX2、ARMではNEONを利用。MygramDB自体にカスタムSIMDコードはなく、高速化はビットマップライブラリ由来です。

60-80%の圧縮はどう実現していますか?

ベースラインは非圧縮のuint32配列(DocIDあたり4バイト)です。2つのメカニズムで削減します:

  1. デルタエンコーディング: 単調増加するDocIDを小さな差分に変換。例:[1000, 1002, 1005][1000, 2, 3]。少ないバイト数でエンコード可能。
  2. Roaring Bitmap圧縮: 密な転置リストはRoaringのコンテナ最適化を使用。連続範囲にはランレングスエンコーディング、散在する密な領域にはビットマップコンテナ。

60-80%は、疎と密が混在する実際の転置リストでの典型的な値です。

レプリケーションとデータ同期

MySQLのbinlogパーサーは自前実装ですか?

はい。MygramDBはMySQL C API(mysql_binlog_open())を直接使用する独自のC++ binlogパーサーを含んでいます。ROW形式のイベント(WRITE_ROWS、UPDATE_ROWS、DELETE_ROWS)およびDDLイベント(TRUNCATE、ALTER TABLE)をパースします。mysql-event-streamは後から独立したCDCライブラリとして切り出されたもので、Node.jsとPythonバインディングを備えています。

レプリケーションの速度は?INSERTから検索可能になるまで?

エンドツーエンドで通常20ms以下:

  • MySQL binlogイベント生成: <1ms
  • ネットワーク転送: <5ms(同一ネットワーク)
  • イベントキュー処理+インデックス更新: 1〜10ms

ボトルネックはネットワークレイテンシです。localhost接続なら5ms以下です。

MySQLフェイルオーバー時の挙動は?

MygramDBは接続断を検知し、指数バックオフ(500msから10s)で再接続します。再接続時:

  1. 現在のGTIDポジションを保持
  2. 新しい接続を確立(ランタイム設定で別ホストへの切り替え可能)
  3. 保存されたGTIDからbinlogレプリケーションを再開。データロスなし

再接続中は検索クエリが一時的にブロックされます(通常1〜5秒)。切断前にキューに入った書き込みイベントは失われません。

ALTER TABLEはどう処理されますか?

MygramDBはbinlog経由でDDLイベントを検知します。TRUNCATE TABLEはインデックスを即座にクリアします。ALTER TABLEとDROP TABLEは警告としてログに記録されます。検索対象カラムのスキーマ変更(リネーム、型変更)を行った場合は手動リビルドが必要です。MygramDBを停止し、ダンプを削除し、再起動してMySQLからの初期スナップショットをやり直してください。

インデックスからの削除はどう処理されますか?

即時削除です。binlog経由でDELETEイベントが到着すると、ドキュメントのN-gramが転置リストから除去され、ドキュメントストアからも削除されます。トゥームストーンや遅延削除はありません。削除されたドキュメントを参照するキャッシュエントリは無効化されます。

障害復旧と永続化

クラッシュ時の復旧は?WALはありますか?

WALはありません。MygramDBはスナップショットベースの永続化を採用しています:

  1. 定期または手動のダンプで、現在のGTIDポジションとともにインデックス全体を保存
  2. 再起動時に最新のダンプをロードし、保存されたGTIDからbinlogレプリケーションを再開

WALより単純で、MygramDBは読み取り専用レプリカなので許容できます。信頼できるデータソースはMySQLです。直近のダンプがない状態でクラッシュした最悪のケースでは、MySQLからのフルリビルド(初期スナップショット+binlogキャッチアップ)になります。

ダンプ設定:

  • dump.interval_sec: 自動保存間隔(0 = 無効、本番推奨: 7200)
  • dump.retain: 保持するダンプ数(デフォルト: 3)

メモリがハードリミットに達するとどうなりますか?

MygramDBはヘルスチェックエンドポイントでメモリ使用量を監視しますが、ハードリミットに達してもクエリの拒否やレプリケーションの停止は行いません。インデックスが利用可能なRAMを超えた場合、OS側のOOM killerがプロセスを終了します。実運用では、全データセットとヘッドルームを確保できるサーバーを用意してください。目安は100万ドキュメントあたり約2.3GB(verify_text: allの場合)。/health/detailやPrometheusメトリクスでメモリ使用量の推移を監視してください。

プロトコルとセキュリティ

クエリプロトコルは何ですか?

TCP上のカスタムテキストプロトコル(デフォルトポート11016)で、Memcachedに似ています。REST/JSON(HTTP、同一ポート)でも利用可能です。

TCP例:

SEARCH articles golang SORT created_at DESC LIMIT 10\r\n

HTTP例:

bash
curl "http://localhost:11016/api/v1/search?table=articles&q=golang&sort=created_at&order=desc&limit=10"

SQLではありません。検索、カウント、ID取得、管理コマンドに絞った最小限のプロトコルです。

TLSや認証はありますか?

検索APIにTLSや認証はありません。アクセス制御はNETWORK_ALLOW_CIDRS(CIDRベースの許可リスト)で行います。MySQLレプリケーション接続はSSL/TLSをサポートしています。

MygramDBはプライベートネットワーク内、リバースプロキシやファイアウォールの背後での運用を前提としています。MemcachedやAUTHなしのRedisと同じモデルです。TLS終端が必要な場合はnginxやロードバランサーの背後に配置してください。

デプロイと監視

Kubernetesで動作しますか?

必要な要素は揃っています。ヘルスチェックエンドポイントが組み込まれています:

  • GET /health/live — Livenessプローブ(プロセス稼働中なら200)
  • GET /health/ready — Readinessプローブ(初期同期中は503、準備完了で200)
  • GET /health/detail — アップタイム、インデックス統計、メモリを含む詳細ステータス

PrometheusメトリクスはGET /metricsで取得可能。Dockerイメージは非rootユーザー(UID 1000)で実行されます。作者はDocker上で運用しており、Kubernetes固有の運用ノウハウは限定的です。

どのようなメトリクスが取得できますか?

GET /metricsでPrometheus形式のメトリクスを提供:

  • クエリ性能(検索/カウント/取得のレイテンシ、QPS)
  • インデックス統計(テーブルごとのターム数、ドキュメント数)
  • キャッシュ統計(ヒット率、メモリ、エビクション)
  • メモリ使用量(RSS、ヒープ)
  • レプリケーション状態(binlogポジション、ラグ、適用イベント数)
  • 接続統計

「単一バイナリ」と言いつつMySQLが必要 — それは本当にシンプルですか?

MygramDBは追加がシンプルです。MySQLが既に存在することを前提としています — より良い検索が必要なアプリケーションの大半はそうです。データベースを置き換えるのではなく、横に並べてデプロイします。環境変数でMySQLを指定したdocker run一発です。スキーマ変更不要、アプリケーション書き換え不要、ETLパイプライン不要。シンプルさは依存ゼロということではなく、統合の容易さにあります。

ベンチマーク

ベンチマークのハードウェア環境は?

110万件のWikipedia記事(英語100万件+日本語10万件)でテスト。MySQL 8.4はDockerでデフォルト設定、MygramDBはverify_text: all(ポストフィルタ有効)でクエリキャッシュ無効。同一Apple M4 Max(128GBユニファイドメモリ)上で実行。M4 Maxのユニファイドメモリ帯域はDDR4より大幅に高く、インメモリのMygramDBに有利に働く点に注意。一般的なDDR4サーバーではMygramDBのレイテンシはやや高くなる可能性があります。

ベンチマークでMySQL側の設定はチューニングしましたか?

していません。MySQLはデフォルトのinnodb_buffer_pool_sizeinnodb_ft_cache_sizeで実行。多くのMySQL環境がデフォルトまたは最小限のチューニングで運用されている実態を反映しています。積極的なチューニングを行っても、MySQL FULLTEXTの根本的なボトルネック(ディスクベースの転置リストスキャンとシングルスレッドクエリ実行)は変わりません。

MySQLはコールドキャッシュとウォームキャッシュのどちらでテストしましたか?

両方です。MygramDBはキャッシュ状態に関わらず一定のレイテンシを示します。コールド/ウォームの分析はMySQL FULLTEXTが遅い理由をご覧ください。

なぜElasticsearchとの実測比較がないのですか?

Elasticsearchの性能はクラスタ構成で大きく変動し、シングルノード比較はミスリーディング、クラスタ比較は再現不可能です。比較ページで定性的なガイダンスを提供しています。

プロジェクト

なぜRustではなくC++ですか?

トレードオフは承知の上です。作者はRustがC++を置き換えるのは時間の問題だと考えており、チーム開発ならRustを選ぶでしょう。MygramDBは個人のOSSプロジェクトで、メモリレイアウトの直接制御が欲しかった — 転置リスト操作、ビットマップ演算、デルタエンコーディングはバイトの配置がわかっていることに意味があります。RAII、unique_ptrによる所有権管理、例外の代わりにExpected<T, Error>を使用し、C++の一般的な落とし穴を軽減しています。他の人にC++の新規プロジェクトを勧めるものではありません。

プロジェクトの成熟度は?

開発は2025年11月に開始。現在のバージョンは1.6.0(2026年4月)で、BM25関連度スコア、ハイライト、ファジー検索、ファセット集計、シノニム展開、セクション単位CRC32を持つV2ダンプ形式、そしてMariaDB binlogレプリケーション対応が追加されました。高トラフィックなサービスで本番利用実績があります(詳細はNDAにより非公開)。リリースはアクティブで、検索機能の拡充、スケーラビリティ(イベント駆動Reactor I/Oモデル)、安定性(スレッドセーフティ修正、レプリケーションのエッジケース)、運用機能(Prometheusメトリクス、ヘルスチェック、Kubernetes対応)に注力しています。

MySQL FULLTEXTからMygramDBへの移行方法は?

MySQLはそのまま維持し、MygramDBを横に立てて、検索クエリだけルーティングします。データ移行不要 — binlogで自動同期。クイックスタートまたは比較 — 移行方法をご覧ください。


その他の質問はGitHubをご確認いただくか、Issueを作成してください。