Jämförelse mellan fulltextssökning i PostgreSQL och Elasticsearch

En databas eller en riktig sökstapel

Sidinnehåll

Det verkliga argumentet handlar inte om om PostgreSQL kan söka text eller om Elasticsearch kan lagra dokument. Båda kan det. Den intressanta frågan är var sökkomplexiteten bör bo.

PostgreSQLs fulltextssökning lever inuti en transaktionell relationell databas med tsvector, tsquery, ordböcker, rangordning och GIN-indeks. Elasticsearch är en distribuerad sökmotor och analysmotor byggd på Lucene, med analyserare, BM25-poängsättning, skalning baserad på skärmar, sammanställningar och nästan realtidsindexering.

postgresql-vs-elasticsearch

Det är olika operativa filosofier innan de ens är olika funktionslistor.

Om du kartlägger detta val mot lagring, rör och operationer, ger den här översikten över datainfrastruktur den bredare systemkontexten.

Vad denna jämförelse egentligen handlar om

På en låg nivå bygger båda systemen på idéer om inverterade index, men de packar in dem på väldigt olika sätt. PostgreSQL rekommenderar GIN som den föredragna indextypen för textsökning och beskriver den som ett inverterat index över lexem i tsvector-värden. Elasticsearch analyserar text-fält och indexerar dem för fulltextssökning, och distribuerar sedan dessa index över skärmar och noder för att skala. I praktiken känns PostgreSQL som sökning inbäddad i din applikationsdatabas, medan Elasticsearch känns som en dedikerad sökmotor med sin egen körning, livscykel och skalningsmodell.

Denna jämförelse handlar mest om inbyggd fulltextssökning i PostgreSQL plus den mycket vanliga hjälpen pg_trgm för ungefärlig matchning. Det här omfånget är viktigt eftersom den bredare PostgreSQL-ekosystemet blir mer sökintensivt över tid. Tillägg som RUM lägger till rikare indexbeteende för frasesökning och rangordningsoptimerade skannar, medan PGroonga utvidgar PostgreSQL med en annan väg för fulltextindexering. Det gör inte native PostgreSQL lika med Elasticsearch, men det betyder att gränsen är mindre statisk än många äldre jämförelser antar.

Min uppfattning är enkel. Sökning är oftast en funktion tills den blir en produktyta. PostgreSQL tenderar att vinna medan sökning fortfarande är en funktion. Elasticsearch tenderar att vinna när sökning blir det användarna bedömer först. Det handlar mindre om varumärken och mer om var relevanslogik, indexeringspolicy och operationell smärta får bo.

Hur PostgreSQLs fulltextssökning fungerar

PostgreSQLs fulltextssökning börjar med att omvandla rå text till lexem. to_tsvector tokeniserar text, normaliserar den genom konfigurerade ordböcker, tar bort stoppord och sparar överlevande lexem med positioner. setweight låter dig märka lexem från olika delar av dokumentet, såsom titel, abstrakt och brödtext, så att dessa delar kan påverka rangordningen olika. PostgreSQL stöder också flera fördefinierade språkkonfigurationer och låter dig bygga egna konfigurationer med parser och ordböcker. Om du vill ha en kompakt SQL-referens medan du implementerar dessa mönster, är den här PostgreSQL-snabbreferensen och den här SQL-snabbreferensen med de mest användbara SQL-kommandona praktiska följare.

Ett typiskt produktionsmönster är en sparad genererad tsvector-kolumn plus ett GIN-index. PostgreSQLs dokumentation är tydlig med att praktisk textsökning oftast kräver ett index, och den visar explicit en sparad genererad kolumn som matar ett GIN-index. Det mönster undviker att beräkna to_tsvector under verifiering och håller frågeytan ren.

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;

På frågesidan ger PostgreSQL dig flera parser eftersom användarinput är mer rörig än vad ingenjörsbloggar ger med sig. to_tsquery är explicit och kraftfull. phraseto_tsquery bevarar ordföljd med operatören <->. websearch_to_tsquery accepterar sökmotorliknande input, förstår citerade fraser, OR och --negation, och kastar aldrig syntaxfel på rå användarinput. PostgreSQL stöder också prefixmatchning genom att bifoga * till ett lexem i to_tsquery.

Rangordning är där native PostgreSQL visar både sin styrka och sitt tak. ts_rank och ts_rank_cd kan använda frekvens, närhet och strukturella vikter, och viktningsmodellen är överraskande bra för många applikationssökningar. Samtidigt noterar PostgreSQLs egna dokument att rangordning kan vara dyr och att inbyggda rangordningsfunktioner inte använder global information. Det är den tysta men viktiga gränsen för native PostgreSQLs fulltextssökning. Den kan rangordna, men relevans är inte tyngdpunkten i motorn.

När PostgreSQL räcker för fulltextssökning

PostgreSQL räcker oftare än dedikerade sökmotorleverantörer vill. Det är särskilt lockande när sökningen stannar mycket nära transaktionella rader, joins, behörigheter och färska skrivningar. PostgreSQLs MVCC-modell ger transaktionell konsistens och läsningar baserade på ögonblicksbild, så samma databas som accepterar skrivningen kan svara på sökningen utan ett Elasticsearch-liknande uppdateringsfönster. När en sökruta egentligen är “hitta poster inne i appen jag precis redigerade”, är det egenskapen viktigare än glansiga relevansdemonstrationer.

Det räcker också när SQL-filtrering är halva funktionen. Statusfilter, tenant-isolering, publiceringsstater, tidsstämpel och relationella joins betyder ofta lika mycket som nyckelordsrelevans i affärssystem. I de fallen beter sig PostgreSQLs fulltextssökning som ett annat indexerat predikat i en relationell frågeplan, inte som en separat plattform som behöver matas och hållas varm. Det är en tråkig arkitektur, och tråkig är ofta rätt sorts snabb.

Hur Elasticsearch fungerar som en sökmotor

Elasticsearch presenterar sig väldigt olika. Dess egna dokument definierar den som en distribuerad sökmotor och analysmotor, skalbar databas och vektordatabas byggd på Apache Lucene, optimerad för hastighet och relevans i produktionsstorlek och opererande i nästan realtid. Elasticsearch delar upp varje index i skärmar, replikerar dessa skärmar och distribuerar dem över noder för att öka indexerings- och frågekapacitet. Det är varför Elasticsearch sällan är “bara ett index”. Det är en klusterarkitektur.

Under huven gör analyserare mest av tunga lyft. En Elasticsearch-analyserare är en sammansättning av teckenfilter, tokeniserare och tokenfilter. Det finns inbyggda analyserare, språkanalyserare och skräddarsydda analyserare, och synonymhantering är en första klass i analysen. Det betyder att sök beteende inte bara handlar om frågan. Det handlar också om hur både dokument och frågor normaliseras innan poängsättning ens börjar.

För en praktisk API-referens medan du implementerar dessa mönster, sammanfattar den här Elasticsearch-snabbreferensen essentiella kommandon och operationella genvägar.

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": {}
    }
  }
}

Query DSL är där Elasticsearch börjar kännas söknativt snarare än databasliknande. bool kombinerar klausuler med must, should, filter och must_not. multi_match kan söka över många fält med fältboost och olika exekveringslägen som best_fields, most_fields, cross_fields, phrase och bool_prefix. Sammanställningar, höjningar och filter kan alla sitta bredvid huvudfrågan i samma begäran. BM25 är standardlikhetsmodellen.

Färskhetsmodellen är också explicit. Elasticsearch är nästan realtid, inte omedelbart sökkonsistent. Nyligen operationer blir synliga för sökning när en uppdatering öppnar en ny segment, och som standard sker den uppdatering varje sekund på index som har sökts nyligen. Elastic’s dokument varnar också att uppdateringar är resurskrävande och rekommenderar att vänta på periodiska uppdateringar eller använda refresh=wait_for när en arbetsflöde behöver läs-efter-skriv-sökbar synlighet. Det är en väldigt annan kontrakt än PostgreSQL.

Varför Elasticsearch oftast rankar komplex sökning bättre

Detta är den djupaste tekniska anledningen varför många team slutligen flyttar från PostgreSQLs fulltextssökning till Elasticsearch. PostgreSQLs inbyggda rangordningsfunktioner använder inte global information, medan Elasticsearch använder BM25 som standard och exponerar fält-specifika likhetssättningar, analyserare, multi-fält frågeformer och en sök DSL designad kring relevansjustering. När sökningen blir mindre om “matchade den” och mer om “varför vann dessa tio resultat”, har Elasticsearch oftast mer uttryckligt utrymme.

Elasticsearch har också en tydlig bias mot denormaliserade dokument. Dess dokumentation om join-fält varnar explicit mot att modellera flera nivåer av relationer för att replikera ett relationellt schema och rekommenderar denormalisering för bättre sökprestanda. Det designvalet förklarar mycket av Elasticsearchs styrkor och frustrationer. Det försöker inte vara PostgreSQL med en snabbare LIKE. Det försöker vara en sökmotor som kan poängsätta och hämta stora dokumentinsamlingar snabbt.

PostgreSQL fulltextssökning vs Elasticsearch på verkliga funktioner

Typotolerans är där de två systemen divergerar skarp. Elasticsearch erbjuder fuzzy-frågor baserade på Levenshtein-redigeringsavstånd och erbjuder också dedikerade förslag- och skriv-efter-typ-fälttyper. PostgreSQLs native fulltextssökning är inte typotolerant i sig. Det vanliga PostgreSQL-svaret är pg_trgm, som lägger till likhetsoperatorer och indexstöd för trigramlikhet, LIKE och ILIKE. Det fungerar bra, men det är en kompositionsstrategi snarare än en integrerad sökmotorfunktionssamling.

Höjning finns i båda stackarna, men implementeringsdetaljerna berättar en historia. PostgreSQL använder ts_headline, vilket kan returnera användbara klipp, men dokumenten noterar att det använder originalet, kan vara långsamt och inte garanterat säkert för direkt insättning i webbsidor. Elasticsearch-höjning kan använda postings-avstånd eller termvektor, vilket är särskilt värdefullt på stora fält eftersom det undviker att analysera full text för varje höjningsbegäran. Kort sagt, PostgreSQL kan höjning, medan Elasticsearch är byggd för att höjning i stor skala.

Facetter och sökanalys är en annan bristlinje. Elasticsearch behandlar sammanställningar som en första klass i sökmodellen, med metrik, burkar och rörledningsammanställningar tillgängliga direkt i sökresponsen. PostgreSQL kan uppenbarligen sammanfatta eftersom det är SQL, men när räknade burkar, histogram och sammansättningsbar sökanalys blir en del av sökproduktens själva, känns Elasticsearch mycket mer nativt. Skillnaden är inte förmåga i princip. Det är hur mycket frågeergonomi och prestandapolitik motorn ägnar åt den arbetsbelastningen.

Autofullföljande följer samma mönster. PostgreSQL kan göra prefixmatchning i to_tsquery, vilket är användbart och ofta tillräckligt för interna verktyg. Elasticsearch går längre med search_as_you_type-fält som automatiskt bygger flera analyserade underfält för prefix- och infix-fullföljande, plus kompletterande förslagare som är syftade för snabba förslag. Det gapet är litet på en adminpanel och stort på en användarriktad upptäcktyta.

Operationell kostnad betyder mer än prestandaskärmar

Den frestande sökmotorfrågan är “Är Elasticsearch snabbare än PostgreSQL för sökning?” Det ärliga svaret är “för vilken form av sökning?” Elasticsearch är konstruerad kring skärmar, repliker, bulkindexering, uppdateringspolicy och livscykelhantering. Elastic’s egna produktionsdokument går djupt på skärmstrategi, storlek på bulk-begäran, indexeringströmmighet, uppdateringsintervall och ILM. PostgreSQL undviker ett andra kluster, men GIN-underhåll är inte gratis. PostgreSQLs dokument varnar att GIN-insättningar kan vara långsamma, att rensning av väntande listor kan orsaka svarstidsfluktuationer, och att autovacuum-strategi betyder något om indexet uppdateras tungt.

Det gör prestandaberättelsen mer nyanserad än vad de flesta jämförelseposter medger. Elasticsearch har oftast mer utrymme för stora topp-N lexikal sökning, facettering, autofullföljande och distribuerad läsvolym eftersom dess arkitektur är dedikerad åt dessa uppgifter. PostgreSQL känns ofta snabbare för relationella applikationsfrågor med strikta färskhetskrav eftersom det inte finns någon andra databas, ingen uppdateringsgräns och ingen synkroniseringsväg att felsöka. Vinnaren är oftast arbetsbelastningens form, inte prestandaskärmen. Det är delvis en inferens, men det följer direkt från PostgreSQLs transaktionella MVCC-modell och Elasticsearchs nästan realtidsskärmbaserade design.

Bör transaktionella data och sökindex bo i samma system? När sökrelevans är måttlig men färskhet, behörigheter och transaktionell sanning är kritiska, har samma-systemsdesign uppenbara fördelar. När sökkvalitet, facettering, synonympolicy, typotolerans och horisontell sökskalning blir första klass produktbryggor, börjar ett andra system se berättisat ut. Elasticsearchs egna skärmstorleksguidance säger att det inte finns en storlek som passar alla och rekommenderar att benchmarka produktionsdata på produktionsmaskinvara. Den meningen fångar tradeoffen perfekt. Elasticsearch köper utrymme genom att be dig att operera mer sök-specifik arkitektur.

Det praktiska slutordet

PostgreSQLs fulltextssökning vinner de första 80 procenten överraskande ofta. Den stöder tokenisering, stoppord, stamning, frasesökning, vikter, rangordning, höjning, genererade sökväktorer, GIN-index och trigram-baserade likhets hjälpar. Kombinerat med PostgreSQLs transaktionella semantik ger det många applikationer en sökstack som är enkel, aktuell och nära datan. För SaaS-backkontor, interna verktyg, moderat innehållssidor och app-nativ sökning, är den kombinationen svår att avfärda.

Elasticsearch blir övertygande när sökning inte bara är en filter utan en produktyta. BM25 som standard, skräddarsydda analyserare, synonymfilter, fuzzy-frågor, multi-fält rangordning, sammanställningar, dedikerade autofullföljandealternativ, stora-fält höjningstrategier och distribuerad skärmbaserad skalning är inte sidofunktioner. De är anledningen motorn existerar. Det är varför Elasticsearch-jämförelser som bara fokuserar på rå latens oftast missar poängen. Den större skillnaden är hur mycket sökproduktlogik motorn är villig att äga.

Den renaste mentala modellen är denna. PostgreSQLs fulltextssökning är utmärkt när sökning tillhör databasen. Elasticsearch är utmärkt när databasen måste mata en sökmotor. De flesta team fokuserar för mycket på hastighet och för lite på misslyckandemoder. Det verkliga valet är var relevansjustering, datafärskhet och operationell komplexitet får bo.