PostgreSQL jako jediná databáze pro tvou AI aplikaci: kdy to stačí a kdy sáhnout vedle
Když si vývojář posedlý AI nástroji poskládá stack pro novou aplikaci, obvykle skončí u pěti služeb: relační databáze, vektorová databáze, Elasticsearch, Redis a něco na fronty. Každá z nich má svůj Docker kontejner, své heslo, své zálohy a svůj způsob, jak se pokazit o půlnoci. A přitom existuje jedna nuda, která tohle všechno umí dohromady: PostgreSQL.
Není to hype. Myšlenka „Postgres for Everything" se objevila koncem srpna 2026 na přední straně Hacker News díky textu Raphaela Bauera, ve kterém shrnuje 20+ let zkušeností s používáním Postgresu jako relační databáze, fulltextu, dokumentové databáze, fronty i vektorového úložiště. V komentářích se objevila klasická výzva „přestaň šetřit na provozu a nasaď Kafku", ale i spousta lidí, kteří přiznali, že jejich produkce běží na přesně tomhle přístupu — a spí.
V tomhle článku ti ukážu, jak tenhle přístup aplikovat na AI aplikace: kde přesně ti Postgres ušetří celé subsystémy, kde jsou limity, a jak rozhodnout, kdy přejít na specializovaný nástroj, dřív než tě to začne bolet.
Proč to dnes má smysl víc než kdy dřív
Argument „jedna databáze na všechno" není nový, ale AI aplikace ho posunuly do jiného kontextu. A dnešní Postgres je navíc jiná generace, než jakou znáš z před pár let: PostgreSQL 18 (vydaný v září 2025) přinesl asynchronní I/O, které sekvenční scany zrychluje až 2–3× na Linuxu pomocí io_uring, nativní podporu UUIDv7 (časově řazené klíče — ideální pro event logy a konverzace, kde B-tree indexy s náhodnými UUIDv4 trpěly), virtuální generované sloupce a SQL standardní JSON_TABLE pro dotazování nad JSON jako nad relační tabulkou. Všechno jsou to funkce, které se v AI aplikacích hodí přesně tam, kde dřív sídlily specializované služby.
Tvůj typický AI produkt dnes potřebuje:
- relační data (uživatelé, objednávky, předplatné),
- vektorová data (embeddingy dokumentů pro RAG),
- fulltext (vyhledávání v dokumentech před tím, než dorazí do LLM),
- frontu (asynchronní AI joby, generování, embedding pipeline),
- cache (uložené odpovědi, rate limiting).
Klasický přístup znamená pět systémů. Postgres přístup znamená jeden. A u malých týmů, které staví AI produkt vedle své denní práce, není řešení otázkou elegance, ale přežití: každý další systém znamená další věc, kterou musíš monitorovat, zálohovat, upgradovat a opravovat, když vypadne.
pgvector: vektorové hledání bez Pinecone
Nejčastější důvod, proč AI aplikace sahají po specializované databázi, jsou embeddingy. Ale rozšíření pgvector je dnes součástí majoritních spravovaných Postgresů (RDS, Cloud SQL, Supabase) a pro typický RAG workload je dostačující:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
content TEXT NOT NULL,
embedding VECTOR(1536) NOT NULL,
metadata JSONB DEFAULT '{}'::jsonb
);
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops);
A dotaz na nejbližší sousedy — jádro každého RAG pipeline:
SELECT id, content,
1 - (embedding <=> $1) AS similarity
FROM documents
ORDER BY embedding <=> $1
LIMIT 10;
Verze pgvector 0.8 navíc výrazně posunula laťku: iterativní prohledávání indexu automaticky doskenuje víc HNSW grafu, dokud nenajde dost výsledků (klíčové u filtrovaných dotazů, kde dřív filtry výsledky vyřízly), opravené odhady plánovače lépe rozhodují mezi ANN indexem a sekvenčním scanem a některé dotazové patterny jsou až 5,7× rychlejší. Paralelní build HNSW indexů zkrátil buildy, které dřív trvaly hodiny, na zlomek času. Poznámka k provozu: řada 0.8.x opravila bezpečnostní chybu CVE-2026-3172 (heap buffer overflow při paralelním buildu) i problémy s VACUUMem HNSW grafů — pokud pgvector provozuješ, měj aktuálně minimálně 0.8.4.
Kde je limit: pgvector pohodlně zvládá miliony vektorů, ale pokud máš desítky milionů dokumentů, potřebuješ agresivní filtrování (hybrid search s metadaty) a máš přísné latencní SLA, specializované vektorové databáze jako Pinecone nebo Qdrant ti dynamiku vyhledávání vymažou. Jinými slovy: do seriózního produktu s tisíci plácejících uživatelů se pgvector hodí; do plošného vyhledávání přes celý internet ne.
Fulltext bez Elasticsearchu
Druhý subsystém, který ti Postgres ušetří, je fulltext. Vestavěný tsvector s GIN indexem zvládne fulltextové hledání v češtině i angličtině, nehledě na to, že hybridní kombinace fulltext + vektorové hledání je dnes standardní RAG technika (BM25 + cosine similarity):
ALTER TABLE documents
ADD COLUMN search_vector tsvector
GENERATED ALWAYS AS (to_tsvector('english', content)) STORED;
CREATE INDEX search_idx ON documents USING GIN (search_vector);
-- Hybridní skóre: fulltext + vektorová podobnost
WITH fulltext AS (
SELECT id, ts_rank(search_vector, query) AS text_score
FROM documents, plainto_tsquery('english', 'postgres scaling') query
WHERE search_vector @@ query
LIMIT 50
)
SELECT d.id, d.content,
f.text_score * 0.4 + (1 - d.embedding <=> $1) * 0.6 AS final_score
FROM documents d JOIN fulltext f ON f.id = d.id
ORDER BY final_score DESC;
Výhoda oproti Elasticsearchu je zásadní: nemusíš synchronizovat data mezi dvěma systémy. Co zapíšeš do Postgresu, je okamžitě vyhledatelné. U AI aplikací, kde dokumenty generuješ nebo aktualizuješ průběžně (embedding pipeline, cron joby), je to o jedno celé selhání míň na světě.
Kdy přejít: pokud plánuješ fulltext přes stovky GB, potřebuješ fasety, složitou analýzu nebo nárazový load z veřejného vyhledávání, Elastic (nebo Typesense) ti dá víc prostoru. Pro interní vyhledávání v produktu je Postgres většinou až překvapivě stačící.
JSONB místo MongoDB
Třetí subsystém: dokumentová databáze. AI aplikace generují nestrukturované výstupy — odpovědi modelů, strukturované extrakce, konverzační historie, konfigurace agentů. JSONB sloupec tohle všechno zvládne s indexovatelnými poli:
CREATE TABLE ai_conversations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id BIGINT REFERENCES users(id),
messages JSONB NOT NULL DEFAULT '[]',
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX conv_gin ON ai_conversations USING GIN (messages);
-- Najdi konverzace, kde padlo jméno konkrétního nástroje
SELECT * FROM ai_conversations
WHERE messages @> '[{"tool": "web_search"}]';
Rozdíl proti MongoDB je hlavně v tom, že můžeš v jednom dotazu spojit dokumentová i relační data — což u AI aplikací znamená třeba „načti konverzaci i s uživatelovým tarifem a limity" bez aplikační logiky, která by to slepovala ze dvou databází.
Kdy přejít: když máš tým, který dokumentové schéma aktivně vyvíjí a potřebuje tooling MongoDB ekosystému. Tehdy je kultivovaný přechod na specializovanou dokumentovou DB legitimní. Ale „Mongo, protože JSON" není argument — JSONB je po výkonu i indexaci srovnatelný.
Fronta bez RabbitMQ
Čtvrtý subsystém: message broker. AI joby (generování, embedding, evaluace) jsou asynchronní z podstaty — LLM volání trvají sekundy až minuty. A tady je ironie: sám používám produkčně Messenger s Doctrine transportem, což je přesně tenhle vzorec — fronta v Postgresu, žádný broker navíc.
Klasický přístup je tabulka s SELECT ... FOR UPDATE SKIP LOCKED:
CREATE TABLE job_queue (
id BIGSERIAL PRIMARY KEY,
type TEXT NOT NULL,
payload JSONB NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
available_at TIMESTAMPTZ NOT NULL DEFAULT now(),
created_at TIMESTAMPTZ DEFAULT now()
);
-- Worker si bezpečně vezme job i s více workery najednou
UPDATE job_queue SET status = 'running', locked_at = now()
WHERE id = (
SELECT id FROM job_queue
WHERE status = 'pending' AND available_at <= now()
ORDER BY id
FOR UPDATE SKIP LOCKED
LIMIT 1
)
RETURNING *;
Tento vzorec zvládne stovky jobů za sekundu, transakční garance (job se objeví ve frontě až s daty, ke kterým patří) a spolehlivost na úrovni, kterou v malém měřítku RabbitMQ nemá šanci překonat. Symfony Messenger tohle má vestavěné jako Doctrine transport — pokud používáš Symfony, frontu v Postgresu máš doma bez jediné řádky konfigurace navíc.
Kdy přejít: když ti frekvence jobů roste do tisíců za sekundu, potřebuješ fan-out do mnoha konzumentů, event sourcing nebo stream processing. Tam je Kafka legitimní. Do té doby je broker, který musíš provozovat a hlídat, jen další věc, která se může pokazit.
Cache: pátý subsystém, který přebývá
V úvodu jsem slíbil pět subsystémů — relační data, vektory, fulltext a frontu jsme pokryli. Ten pátý, cache, má specifické postavení: lokální cache LLM odpovědí většinou nepatří do databáze vůbec. Pokud chceš kešovat výsledky promptů, stačí tabulka s hashem promptu a odpovědí:
CREATE TABLE llm_cache (
prompt_hash TEXT PRIMARY KEY,
model TEXT NOT NULL,
response JSONB NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
-- Cache hit před voláním LLM
SELECT response FROM llm_cache
WHERE prompt_hash = $1 AND created_at > now() - interval '7 days';
Proč to tak dělat: přeskočené volání LLM ušetří 0,5–5 Kč na požadavek a stovky milisekund latence. U opakujících se dotazů (interní nástroje, často kladené firemní otázky) je hit ratio 30–60 % běžné. A Redis si drž pro submilisekundové scénáře — session storage s TTL, rate limiting pod vysokou zátěží. Pokud tvoje cache potřeba je „ulož výsledek a vrátit ho do pěti minut", Postgres tabulka stačí a nemáš další systém na hlídání.
Kde se s tím nepošťouchat
Abych nebyl jen propagandou: jsou věci, na které Postgres není a nebude. Vysokofrekvenční cache s submilisekundovými odezvami (Redis), grafové dotazy s vícenásobnými hopy (Neo4j), heavy OLAP nad terabyty (ClickHouse). Pokud tvůj AI produkt skutečně potřebuje některou z těchto kategorií, integrace specializovaného nástroje ti dá skutečnou hodnotu.
Přístup „Postgres for Everything" neznamená, že bys měl ignorovat limity technologie. Znamená to opačné pravidlo: specializovaný nástroj nasazuj až ve chvíli, kdy máš měřitelný důvod, ne jako výchozí stav. Většina AI aplikací k tomu důvod nikdy nedospěje — buď proto, že nemají dostatečný objem dat, nebo proto, že nemají dostatek provozních kapacit na další systém.
Praktický plán, jak začít
Pokud tě tahle filozofie zaujala, doporučuju postup ve třech krocích:
- Nasaď jeden Postgres a drž se ho. Relační data, JSONB pro dokumenty, pgvector pro embeddingy, fulltext přes tsvector, Doctrine transport pro frontu. Na PostgreSQL 18 využij UUIDv7 primární klíče a
JSON_TABLEmísto aplikačního rozbalování JSONB. Všechno v jedné instanci, jedněch zálohách, jednom monitoringu. - Měř, neřeš. Nastav si pg_stat_statements a sleduj pomalé dotazy. Když vektorové hledání přesahuje 100 ms p95 nebo fronta roste rychleji, než ji workery zvládají, máš číslo na stole — ne pocit.
- Přecházet až s daty. Až čísla ukážou limity, přesuň pouze ten jeden subsystém, který limituje. Nasaď Qdrant jen pro vektorové hledání, nech fulltext v Postgresu. Migrace jednoho subsystému je projekt na dny; migrace celého stacku je projekt na měsíce.
Závěr
Myšlenka „PostgreSQL for Everything" není o tom, že by Postgres byl nejlepší na všechno. Je o opačném přístupu k architektuře: místo aby sis vybral pět nejlepších nástrojů pro pět úloh, vybereš jeden dostatečný nástroj pro všech pět a ušetřenou energii investuješ do produktu. U AI aplikací, kde je nejistota v každém kroku (změní se model, změní se ceny, změní se prompt), je flexibilita jednoduché architektury víc než výkon specializované.
A pokud si chceš přečíst originál, který tuhle vlnu spustil: PostgreSQL for Everything od Raphaela Bauera.