問題はこうだ:求人アラートシステムを作っている。
1,000万人のユーザーが「Java + リモート」や「Python + ニューヨーク」のような検索を保存している。
毎日10万件の新しい求人が入ってくる。
ナイーブなアプローチ:
各新求人(10万/日)について:
各保存検索(1,000万)について:
求人が検索にマッチするか確認
合計: 100,000 × 10,000,000 = 1日あたり1兆回の操作
これは約1秒あたり1,150万回の操作。
ステータス: 無理。
Elasticsearchはこれをパーコレーターと呼ぶ。キーとなる洞察:
従来の検索(知っているもの):
多くのドキュメント(商品、記事、求人)がある
ユーザーがクエリを実行
マッチするドキュメントを返す
パーコレーター(逆転):
多くのクエリ(保存検索、アラート)がある
新しいドキュメントが来る
マッチするクエリを返す
検索の逆だ。
O(n × m)からO(n × log m)になる。「不可能」と「簡単」の違い。
Elasticsearchがどのクエリがマッチするか教える。それらのユーザーに通知を送る。ミリ秒で完了、日数ではなく。
求人サイト(LinkedIn、Indeed):
ドキュメント: 新しい求人投稿
クエリ: 5,000万の保存求人アラート
アクション: 「この求人にマッチするアラートのユーザーに通知」
パーコレーターなし: 求人ごとに5,000万アラートをチェック = 不可能
パーコレーターあり: 1ドキュメントをクエリインデックスに対してチェック = 高速
EC(Amazon、eBay):
ドキュメント: 商品価格が$450に下落
クエリ: 「価格が$500未満になったら通知」
アクション: 「この1,247人がこのアラートを求めている」
ニュースアグリゲーター:
ドキュメント: 「暗号通貨規制」についての新記事
クエリ: 購読者フィルター
アクション: 「これらの50,000人の購読者に送信」
モニタリング/アラート(Datadog、PagerDuty):
ドキュメント: error_rate=7%のログエントリ
クエリ: "Error rate > 5% AND service = 'payments'"
アクション: 「オンコールエンジニアをページ」
パーコレータークエリはインデックスするのが高コスト。保存検索ごとに1つのインデックスされたクエリ。
課題:1つのインデックスに1,000万クエリは遅くなる。
カテゴリでシャード: エンジニアリング求人はエンジニアリングアラートインデックスだけをチェック。マーケティング求人はマーケティングだけ。検索空間が10分の1に。
ロケーションでシャード: NYC求人はNYCアラートだけ。SF求人はSFだけ。検索空間が50分の1に。
ユーザーごとのクエリ数を制限: 無料枠は5つの保存検索、Pro枠は50。インデックス成長をコントロール。
従来の検索は、多くのドキュメントに対して少数のクエリがあるとき。ユーザーが検索を開始し、リアルタイムでオンデマンド。例:Google検索、ECブラウジング、ログ探索、ダッシュボードクエリ。
**パーコレーター(リバース検索)**は、少数のドキュメントに対して多くのクエリがあるとき。システムがマッチングを開始し、バッチまたはストリーミングで処理。例:求人アラート、価格下落通知、リアルタイムアラートルール、規制モニタリング。
入ってくるドキュメントよりクエリが多いなら、検索を逆転させろ。
ドキュメントよりクエリが多いとき、検索を逆転させろ。
— blanho
冗長パイプライン、インテリジェントなセグメント選択、カスタムストレージレイヤー — Netflix Live Originアーキテクチャの内側。
ロギング、認証、リトライ、レート制限 — 最初に設計しないが、後で全員が苦しむもの。
どのアーキテクチャも1つの問題を解決し、3つの新しい問題を生む。コミットする前に誰も教えてくれないことがここにある。
// ステップ1: ユーザーが求人アラートを保存したら、クエリとしてインデックス
PUT /alerts/_doc/user_123
{
"query": {
"bool": {
"must": [
{ "match": { "title": "java" }},
{ "match": { "location": "remote" }}
]
}
}
}
PUT /alerts/_doc/user_456
{
"query": {
"bool": {
"must": [
{ "match": { "title": "python" }},
{ "match": { "location": "new york" }}
]
}
}
}
// 1,000万アラート = 1,000万インデックスされたクエリ// ステップ2: 新求人が来たらパーコレート
GET /alerts/_search
{
"query": {
"percolate": {
"field": "query",
"document": {
"title": "Senior Java Developer",
"location": "Remote",
"salary": 150000
}
}
}
}
// レスポンス:
{
"hits": [
{ "_id": "user_123", "_score": 2.5 }, // マッチ!
{ "_id": "user_789", "_score": 1.8 } // マッチ!
]
}