PostgreSQL フルテキスト検索と Elasticsearch の比較

1 つのデータベースか、本物の検索スタックか

目次

本当の議論の焦点は、PostgreSQL がテキスト検索できるかどうか、あるいは Elasticsearch がドキュメントを保存できるかどうかではありません。両者とも可能です。興味深いのは、検索の複雑性がどこに存在すべきかという点です。

PostgreSQL の全文検索は、tsvectortsquery、辞書、ランク付け、GIN インデックスを備えたトランザクショナルなリレーショナルデータベースの内部に存在します。一方、Elasticsearch は Lucene 上に構築された分散型検索・分析エンジンであり、アナライザー、BM25 スコアリング、シャードベースのスケーリング、集約、ニアリアルタイムインデックスをサポートしています。

postgresql-vs-elasticsearch

これらは、機能リストの違いよりも先に、運用哲学の違いです。

この選択をストレージ、パイプライン、運用にマッピングする場合、このデータインフラストラクチャの概要 が、より広いシステムコンテキストを提供します。

この比較が実際に何について言っているのか

低レベルでは、両システムは逆インデックスのアイデアに依存していますが、パッケージ化の仕方は非常に異なります。PostgreSQL は GIN を推奨するテキスト検索インデックスタイプとして推奨し、それを tsvector 値上の語彙(レクセム)に対する逆インデックスとして説明しています。Elasticsearch は text フィールドを解析し、全文検索のためにインデックス化してから、スケーリングのためにシャードとノードに分散します。実際には、PostgreSQL はアプリケーションデータベースに埋め込まれた検索のように感じられ、Elasticsearch は独自のランタイム、ライフサイクル、スケーリングモデルを持つ専用検索プラットフォームのように感じられます。

この比較は、主にネイティブの PostgreSQL 全文検索と、ファジーな一致に非常に一般的な pg_trgm ヘルパーについてです。その範囲は重要です。なぜなら、より広い PostgreSQL エコシステムは時間の経過とともに検索重視のものになりつつあるからです。RUM などの拡張機能は、フレーズ検索やランク付け指向のスキャンのためにより豊かなインデックス動作を追加し、PGroonga は PostgreSQL に別の全文インデックスパスを拡張します。これはネイティブの PostgreSQL が Elasticsearch と同等になるという意味ではありませんが、境界線は多くの古い比較が想定するほど静的ではないことを意味します。

私の意見に基づく枠組みはシンプルです。検索は、通常、機能がプロダクトの表面になるまで機能です。PostgreSQL は、検索がまだ機能である段階で勝つ傾向があります。Elasticsearch は、検索がユーザーが最初に評価するものになったときに勝つ傾向があります。これはブランド名よりも、関連性のロジック、インデックスポリシー、運用上の負担がどこに存在することを許容するかという点に関係しています。

PostgreSQL 全文検索の仕組み

PostgreSQL 全文検索は、生テキストを語彙(レクセム)に変換することから始まります。to_tsvector はテキストをトークン化し、設定された辞書を通じて正規化し、ストップワードを削除し、生存した語彙を位置情報とともに保存します。setweight を使用すると、ドキュメントの異なる部分(タイトル、要約、本文など)からの語彙にラベルを付け、それらの部分がランク付けに異なる影響を与えるようにできます。PostgreSQL は複数の事前定義された言語設定をサポートし、パーサーと辞書を使用してカスタム設定を構築することもできます。 これらのパターンを実装する際にコンパクトな SQL リファレンスが必要であれば、この PostgreSQL チートシート最も有用な SQL コマンドを含む SQL チートシート が実用的な相棒になります。

典型的な本番環境のパターンは、保存された生成 tsvector カラムと GIN インデックスです。PostgreSQL のドキュメントは、実用的なテキスト検索には通常インデックスが必要であると明確に述べており、GIN インデックスに供給される保存された生成カラムを明示的に示しています。このパターンは、検証時に to_tsvector を再計算することを避け、クエリ表面をクリーンに保ちます。

alter table posts
  add column search_vector tsvector
  generated always as (
    setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
    setweight(to_tsvector('english', coalesce(summary, '')), 'B') ||
    setweight(to_tsvector('english', coalesce(body, '')), 'D')
  ) stored;

create index posts_search_idx
  on posts using gin (search_vector);

select id,
       title,
       ts_rank_cd(
         search_vector,
         websearch_to_tsquery('english', '"query planner" -mysql')
       ) as rank
from posts
where search_vector @@ websearch_to_tsquery('english', '"query planner" -mysql')
order by rank desc
limit 20;

クエリ側では、PostgreSQL は、ユーザーの入力はエンジニアリングブログが認めるよりも混沌としているため、いくつかのパーサーを提供します。to_tsquery は明示的で強力です。phraseto_tsquery<-> 演算子を使用して単語の順序を保持します。websearch_to_tsquery は検索エンジンのような入力を受け付け、引用されたフレーズ、OR、および - 否定を理解し、生ユーザー入力に対して構文エラーを絶対に発生させません。PostgreSQL は、to_tsquery の語彙に * を付けることでプレフィックス一致もサポートします。

ランク付けは、ネイティブな PostgreSQL がその強さと天井の両方を示す場所です。ts_rankts_rank_cd は、頻度、近接性、構造的な重みを使用でき、重み付けモデルは多くのアプリケーション検索タスクに対して驚くほど優れています。同時に、PostgreSQL 自身のドキュメントは、ランク付けが高価になり得ることを注記しており、組み込みのランク付け関数はグローバルな情報を使用しないと述べています。これは、ネイティブな PostgreSQL 全文検索の静かだが重要な限界です。ランク付けは可能ですが、関連性はエンジンの重心ではありません。

PostgreSQL が全文検索に十分な場合

PostgreSQL が十分なケースは、専用検索ベンダーが望むよりも頻繁にあります。検索がトランザクショナルな行、結合、権限、新しい書き込みの非常に近くにとどまっている場合、特に魅力的です。PostgreSQL の MVCC モデルはトランザクショナルな一貫性とスナップショットベースの読み取りを提供するため、書き込みを受け付けるのと同じデータベースが、Elasticsearch スタイルのリフレッシュウィンドウなしで検索に応答できます。検索ボックスが本当に「私がたった今編集したアプリ内のレコードを見つける」ものである場合、その特性は、光沢のある関連性デモよりも重要です。

また、SQL 过滤が機能の半分を占める場合にも十分です。ステータスフィルタ、テナントの分離、公開状態、タイムスタンプ、リレーショナル結合は、ラインオブビジネスシステムにおいて、キーワードの関連性と同様に重要な役割を果たすことが多いです。その場合、PostgreSQL 全文検索は、別のプラットフォームのように給餌して温めておく必要のあるものではなく、リレーショナルクエリプラン内の別のインデックス付き述語のように動作します。それは退屈なアーキテクチャですが、退屈はしばしば適切な種類の速さです。

Elasticsearch が検索エンジンとしてどのように機能するか

Elasticsearch は非常に異なる姿を呈します。その自身のドキュメントでは、分散型検索および分析エンジン、スケーラブルなデータストア、Apache Lucene 上に構築されたベクトルデータベースとして定義しており、本番規模での速度と関連性に最適化され、ニアリアルタイムで動作していると述べています。Elasticsearch は各インデックスをシャードに分割し、それらのシャードを複製し、インデックス化およびクエリ容量を増やすためにノードに分散します。これが、Elasticsearch が単なる「インデックス」ではない理由です。それはクラスターアーキテクチャです。

裏側では、アナライザーが最も重い作業を行います。Elasticsearch アナライザーは、文字フィルタ、トークナイザー、トークンフィルタの構成です。組み込みアナライザー、言語アナライザー、カスタムアナライザーがあり、同義語の処理は分析の一級市民です。つまり、検索動作はクエリだけではありません。スコアリングが開始される前に、ドキュメントとクエリの両方がどのように正規化されるかも関係します。

これらのパターンを実装する際のハンズオン API リファレンスとして、この Elasticsearch チートシート には、必須のコマンドと運用のショートカットがまとめられています。

PUT posts
{
  "mappings": {
    "properties": {
      "title":   { "type": "text" },
      "summary": { "type": "text" },
      "body":    { "type": "text" },
      "tags":    { "type": "keyword" }
    }
  }
}

GET posts/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "query planner",
            "fields": ["title^3", "summary^2", "body"],
            "type": "best_fields"
          }
        }
      ],
      "must_not": [
        { "match": { "body": "mysql" } }
      ]
    }
  },
  "aggs": {
    "by_tag": {
      "terms": {
        "field": "tags"
      }
    }
  },
  "highlight": {
    "fields": {
      "body": {}
    }
  }
}

クエリ DSL は、Elasticsearch がデータベースのようにではなく、検索ネイティブに感じはじめる場所です。boolmustshouldfiltermust_not で節を結合します。multi_match は、フィールドブーストと best_fieldsmost_fieldscross_fieldsphrasebool_prefix などの異なる実行モードを使用して、多くのフィールドを検索できます。集約、ハイライト、フィルタはすべて、メインクエリと同じリクエスト内で共存できます。BM25 がデフォルトの類似度モデルです。

鮮度モデルも明示的です。Elasticsearch はニアリアルタイムであり、すぐに検索一貫ではありません。最近の操作は、リフレッシュが新しいセグメントを開いたときに検索に可見になり、デフォルトでは、最近検索されたインデックスに対してそのリフレッシュは 1 秒ごとに発生します。Elastic のドキュメントはまた、リフレッシュがリソース集約的であり、ワークフローが書き込み後の読み取り検索可視性を必要とする場合は、周期的なリフレッシュを待つか、refresh=wait_for を使用することを推奨すると警告しています。これは PostgreSQL と非常に異なる契約です。

なぜ Elasticsearch は通常、複雑な検索をより良くランク付けするのか

これは、多くのチームが最終的に PostgreSQL 全文検索から Elasticsearch へ移行する最も深い技術的な理由です。PostgreSQL の組み込みランク付け関数はグローバルな情報を使用しない一方、Elasticsearch はデフォルトで BM25 を使用し、フィールド固有の類似度設定、アナライザー、マルチフィールドクエリフォーム、関連性チューニング围绕して設計された検索 DSL を公開します。検索が「一致したか」から「なぜこれらの 10 件の結果が勝ったのか」へと変化すると、Elasticsearch は通常、より表現的な余地を持っています。

Elasticsearch はまた、正規化されていないドキュメントへの明確なバイアスを持っています。その結合フィールドのドキュメントは、リレーショナルスキーマを複製するために複数のレベルの関係をモデル化することを明示的に警告し、より良い検索パフォーマンスのために正規化を推奨しています。その設計選択は、Elasticsearch の強さと不満の多くを説明します。それは、より高速な LIKE を持つ PostgreSQL になろうとはしていません。それは、大量のドキュメントコレクションを迅速にスコア付けし、検索できる検索エンジンになろうとしています。

実際の機能における PostgreSQL 全文検索と Elasticsearch の比較

タイプミス耐性は、両システムが鋭く分かれる場所です。Elasticsearch は、Levenshtein 編集距離に基づくファジークエリを提供し、さらに専用の提案およびタイピング中フィールドタイプを提供します。PostgreSQL ネイティブ全文検索は、それ自体ではタイプミス耐性ではありません。通常の PostgreSQL の答えは pg_trgm であり、これはトリグラム類似性、LIKEILIKE に対する類似性演算子とインデックスサポートを追加します。それはうまく機能しますが、それは統合された検索エンジン機能セットではなく、構成戦略です。

ハイライトは両スタックに存在しますが、実装の詳細が物語を語ります。PostgreSQL は ts_headline を使用し、これは有用なスニペットを返すことができますが、ドキュメントは元のドキュメントを使用し、遅くなる可能性があり、ウェブページに直接挿入しても安全が保証されないと注記しています。Elasticsearch のハイライトは、 postings オフセットまたはタームベクトルを使用でき、これは大規模なフィールドにおいて特に価値があり、すべてのハイライト要求ごとに全文を再解析することを回避します。要するに、PostgreSQL はハイライトできますが、Elasticsearch は大規模なハイライトのために構築されています。

ファセットと検索分析はもう一つの断層線です。Elasticsearch は、集約を検索モデルの一級市民として扱い、指標、バケット、パイプライン集約を直接検索レスポンスで利用可能です。PostgreSQL は SQL であるため明らかに集約できますが、カウントされたバケット、ヒストグラム、組み込み検索分析が検索プロダクト自体の一部になったときに、Elasticsearch ははるかにネイティブに感じられます。違いは原理的な能力ではありません。それは、エンジンがそのワークロードにどれだけクエリ人体工学とパフォーマンスポリシーを費やしているかです。

オートコンプリートも同じパターンに従います。PostgreSQL は to_tsquery でプレフィックス一致を実行でき、これは有用で、内部ツールにはしばしば十分です。Elasticsearch は、プレフィックスとインフィックス完了のために自動的に複数の解析済みサブフィールドを構築する search_as_you_type フィールドでさらに進み、高速な提案のために目的作られた完了サジェスターを提供します。そのギャップは管理パネルでは軽微ですが、ユーザー向け発見表面では重大です。

運用コストはベンチマークスクリーンショットよりも重要です

誘惑的な検索エンジン質問は「Elasticsearch は検索に対して PostgreSQL より速いか?」です。正直な答えは「どのような形状の検索か?」です。Elasticsearch は、シャード、レプリカ、バルクインデックス、リフレッシュポリシー、ライフサイクル管理围绕して設計されています。Elastic 自身の本番ドキュメントは、シャード戦略、バルク要求サイズ、インデックススループット、リフレッシュ間隔、ILM について深く掘り下げています。PostgreSQL は第 2 クラスターを回避しますが、GIN 維持は無料ではありません。PostgreSQL のドキュメントは、GIN 挿入が遅くなる可能性があり、保留リストのクリーンアップが応答時間の揺らぎを引き起こす可能性があり、インデックスが頻繁に更新される場合は autovacuum 戦略が重要だと警告しています。

それは、パフォーマンス物語が多くの比較投稿が認めるよりも微妙であることを意味します。Elasticsearch は、そのアーキテクチャがそれらのタスクに専念しているため、大規模なトップ N 語彙検索、ファセット、オートコンプリート、分散読み取りボリュームに対して通常、より多くの余白を持っています。PostgreSQL は、第 2 データストア、リフレッシュ境界、デバッグする同期パスがないため、厳格な鮮度要件を持つリレーショナルアプリケーションクエリに対して通常、より速く感じられます。勝者は通常、ワークロードの形状であり、ベンチマークスクリーンショットではありません。それは部分的に推論ですが、PostgreSQL のトランザクショナル MVCC モデルと Elasticsearch のニアリアルタイムシャードベース設計から直接導かれます。

トランザクショナルデータと検索インデックスは同じシステムに存在すべきですか?検索関連性が控えめだが、鮮度、権限、トランザクショナル真実が重要な場合、同一システム設計は明らかな利点を持っています。検索品質、ファセット、同義語ポリシー、タイプミス耐性、水平検索スケーリングが第一級のプロダクト懸念になった場合、第 2 システムは正当化され始めます。Elasticsearch 自身のシャードサイズガイドラインは、万能の戦略はなく、本番ハードウェアで本番データをベンチマークすることを推奨すると述べています。その文はトレードを完璧に捉えています。Elasticsearch は、より検索固有のアーキテクチャを運用することを求めることで余買います。

実践的な結論

PostgreSQL 全文検索は、驚くほど頻繁に最初の 80% を勝ち取ります。トークン化、ストップワード、語幹抽出、フレーズクエリ、重み、ランク付け、ハイライト、生成検索ベクトル、GIN インデックス、トリグラムベースの類似性ヘルパーをサポートします。PostgreSQL のトランザクショナルセマンティクスと組み合わされ、多くのアプリケーションにシンプルで最新でデータに近い検索スタックを提供します。SaaS 管理画面、内部ツール、中程度のコンテンツサイト、アプリネイティブ検索のために、その組み合わせは容易に否定できません。

Elasticsearch は、検索が単なるフィルタではなく、プロダクトの表面になったときに説得力を持ちます。デフォルトの BM25、カスタムアナライザー、同義語フィルタ、ファジークエリ、マルチフィールドランク付け、集約、専用オートコンプリートオプション、大規模フィールドハイライト戦略、分散シャードベーススケーリングは、サイド機能ではありません。それらはエンジンが存在する理由です。それが、生レイテンシに焦点を当てた Elasticsearch 比較が通常、要点を見逃す理由です。より大きな違いは、エンジンがどれだけの検索プロダクトロジックを所有する意思があるかです。

最も清潔なメンタルモデルはこれです。PostgreSQL 全文検索は、検索がデータベースに属する場合に優れています。Elasticsearch は、データベースが検索プラットフォームに供給しなければならない場合に優れています。多くのチームは速度に過剰焦点を当て、障害モードに焦点を当てすぎません。本当のトレードは、関連性チューニング、データ鮮度、運用複雑性がどこに存在することを許容するかです。