Apache AGE を使用したグラフベースの動画リレーションシップ・クエリの構築
私たちのシステムで最もコストのかかるクエリは、単純な「関連動画を表示」パネルでした。
ViralVidVault では、動画のトレンドを追跡しています。共有チャンネルや同時視聴セッションを通じて関連動画を見つける処理が、データベースのパフォーマンスを著しく低下させていることが分かりました。SQLite で再帰的な結合(recursive joins)を試みましたが、1ホップ(1 hop)なら機能したものの、2ホップになるとデータが爆発的に増加しました。1つのクエリで数十万行が生成され、ワーカーがタイムアウトし始めたのです。
データはグラフ構造です。私たちはそれを無理やりテーブルに押し込もうとし、その代償を払っていました。
私たちはリレーションシップ層を Apache AGE に移行しました。これは PostgreSQL 用の openCypher 拡張機能です。PHP 8.4 のアプリケーションと SQLite ストレージはそのまま維持しました。
結果: • 関連パネルのレイテンシが 900ms から 40ms 未満に低下しました。 • 複雑な2ホップのトラバーサル(走査)が、わずか数ミリ秒で完了するようになりました。 • AGE は Postgres 内で動作するため、運用負荷は変わりませんでした。
なぜスタンドアロンのグラフデータベースではなく、Apache AGE を使うのか?
運用の簡素化 バックアップやセキュリティ対策のために新しいデータベースを用意する必要はありません。AGE は、既存の Postgres のセットアップ、コネクションプール、およびセキュリティルールを利用します。
ネイティブなグラフクエリ SQL では可変長のパスを扱うために複雑な再帰が必要ですが、Cypher では単純なパターンとして記述できます。40行の再帰的な SQL ブロックが、わずか 6 行の Cypher クエリになりました。
パフォーマンスの向上 グラフエンジンは隣接関係(adjacency)をインデックス化します。一致しないパスの拡張を停止するため、以前のシステムをクラッシュさせたデータファンアウト(データの爆発的な拡散)を防ぐことができます。
移行から得た重要な教訓: エントリポイントとなるプロパティには必ずインデックスを貼ってください。トラバーサルを開始するために使用する ID にインデックスが貼られていないと、AGE はフルスキャンを実行してしまいます。これでは、どんなに優れたグラフクエリであっても低速になってしまいます。
私たちはグラフをリードモデル(read model)として使用しています。生データは SQLite に保持したまま、Python スクリプトを使用して両者を同期させています。これにより、グラフを高速かつ軽量に保ち、再構築も容易にしています。
もし再帰的な SQL クエリが複雑になりすぎているなら、リレーショナルモデルと戦うのはやめましょう。現在のストレージの傍らに、小さなグラフ投影(graph projection)を構築するのです。
