Skip to Content
Start Free

A Beginner's Guide to Vector Databases

A Beginner's Guide to Vector Databases
A Beginner's Guide to Vector Databases

Highlights

  • A vector database finds things that mean the same thing, even when they share no words at all.
  • Search for "service will not start" and it can return a doc called "crash loop troubleshooting." No keyword matched. It still worked.
  • Every answer is slightly approximate. That is on purpose, because a perfect search is far too slow.
  • Most teams do not need a new database. If you run Postgres, one extension gives you vector search today.
  • HNSW is the index to pick in 2026. It is faster, more accurate, and you can build it before loading any data.
  • Filtering by version or date fixes more bad results than tuning ever will.
  • Your embedding model sets the size of your vectors. Swap the model later and you re embed everything.

A team spent three weeks picking a vector database. They compared four products on how fast each one searched a hundred million vectors. Then they launched with eleven thousand documents. At that size, every option they tested would have answered in under ten milliseconds. So would a plain table with no index at all.

The comparison was careful. It just measured the one thing their problem did not have. This post starts with what a vector database really stores. Then how the search works. Then how to build one in a database you probably already run. Three worked examples follow, at three very different sizes, because size is what actually decides.

What a vector database is

A vector database stores embeddings and finds the closest ones to your query. It answers one question, fast: which of these is most like this?

It gives you a very good answer instead of a perfect one. That trade is deliberate, and we will get to why.

Start with the embedding

An embedding is a list of numbers that captures meaning.

You send text to a model. It hands back a list of maybe 1,536 numbers. Send similar text and you get similar numbers. Send something unrelated and the numbers land far away.

That is the trick. Meaning becomes distance.

Here is what it buys you. Someone searches for "service will not start." Your doc is titled "crash loop troubleshooting." Not one word matches. Keyword search finds nothing. Vector search finds it easily, because both sit close together in number space.

Did you know

You do not choose how many numbers go into an embedding. The model does. One gives you 384, another 1,536, another 3,072. You take what it hands you. This matters more than it sounds. That number sets your storage, your memory use, and your query cost. And you cannot mix models. Vectors from two different models are not comparable at all. Results look broken rather than merely wrong, which makes it a horrible bug to chase. So pick your embedding model with more care than your database.

Why the search is approximate

To find the true closest vector, you compare against every single one.

With ten thousand vectors, fine. Milliseconds. With ten million, far too slow for a live request.

So vector indexes cheat on purpose. They arrange the vectors so a search can skip most of them. Now and then they miss a true best match.

We measure that with recall. Recall of 0.95 means you got nineteen of the top twenty. One slightly worse result slipped in.

For search, nobody notices. For anything that must be complete and exact, this is the wrong tool.

Exact searchApproximate search
How it works Checks every vector Skips most of them
Recall Perfect Usually 0.90 to 0.99
Speed at 10 million Too slow to use A few milliseconds
Use it for Small sets, or exact work Almost everything else

The two indexes to know

Both ship with pgvector. Both turn up in other databases under the same names. Learn them once and the knowledge travels.

HNSW builds a layered map. The top layer is sparse, so a search takes big jumps across the space. Each layer below is denser and narrows things down. Think of finding a house: first the city, then the street, then the number.

IVFFlat splits vectors into clusters, then searches only the nearest few. Faster to build, lighter on memory, and it needs your data present before you can build it.

HNSWIVFFlat
Shape A layered map you walk down Clusters you pick from
Speed and accuracy Better at both Fine, and tunable
Build time Slower Faster
Memory More Less
Needs data first No Yes
Pick this when Almost always Huge write volume, tight memory

Go with HNSW unless you have measured a reason not to. Two settings matter: m and ef_construction. Start at 16 and 64.

Watch out

Do not tune those numbers yet. The defaults are good. Tuning buys very little and costs build time and memory straight away. Measure first. Run known queries against an exact search, then against your index, and compare. If accuracy is already fine, tuning is pure cost. Teams burn days on settings no user could ever notice.

Build one right now

Here is the fastest way to understand any of this. Build it in Postgres.

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE chunks (
    id          bigserial PRIMARY KEY,
    document_id text NOT NULL,
    section     text,
    version     text,
    content     text NOT NULL,
    embedding   vector(1536)
);

Look at that table. One vector column, four ordinary ones. Those ordinary columns are the whole reason to do this in Postgres. You will see why shortly.

The vector(1536) type locks in the size. Send a vector of the wrong length and Postgres rejects it. That catches a model swap before it quietly ruins your data.

Now the index.

CREATE INDEX chunks_embedding_idx ON chunks
    USING hnsw (embedding vector_cosine_ops)
    WITH (m = 16, ef_construction = 64);

The vector_cosine_ops part tells the index which distance measure you plan to use. It must match the operator in your query. If it does not, Postgres quietly ignores the index and scans everything. No error. Just a slow query and a confused afternoon.

And here is the search.

SET hnsw.ef_search = 100;

SELECT document_id, section, content,
       1 - (embedding <=> $1) AS similarity
FROM chunks
WHERE version = '2.4'
ORDER BY embedding <=> $1
LIMIT 5;

The <=> operator is cosine distance. Order by it, take the top five, and that is a similarity search. Subtracting from one flips distance into similarity, which reads better to humans.

Now look at that WHERE clause again. Most tutorials skip it. It matters more than anything else on this page.

Quick tip

A vector column with no index still works. Postgres just scans the table. Up to about ten thousand rows that is genuinely fine, sometimes even faster. So load your data and query it before adding any index. You get correct results straight away. You get a perfect baseline too, since a full scan is exact by definition. Best of all, you find out whether you even have a speed problem.

Filtering beats tuning

Here is a bug you will meet.

Your support bot keeps answering about the wrong product version. Docs for version 2 and version 3 use nearly identical wording, so they sit right beside each other in number space. Retrieval is doing exactly what you asked. Both passages really are about the question.

No amount of better search fixes this. A WHERE version = '2.4' fixes it instantly.

That is why keeping vectors next to normal columns is so useful. Version, date, team, document type: all just columns.

One catch. Filters and approximate search interact awkwardly. Ask for the top five, filter them, and you might end up with two. Postgres handles this with iterative scanning, which keeps walking the index until it has enough surviving matches.

SET hnsw.iterative_scan = relaxed_order;

That is one line. If you take a single setting from this post into production, take that one.

Learn vector databases properly, end to end

The idea is simple. The details are where projects stall. The Vector Database for GenAI course on KodeKloud covers foundations, embeddings, similarity, vector storage on AWS S3, the wider landscape, and how these databases work inside, all with hands on labs.

Course

Vector Database for GenAI

Foundations, embeddings, similarity, vector storage on AWS S3, the wider landscape, and how these databases work inside. All with hands on labs.

Vector DBAICloud
Explore the course →

Three examples, worked through

Same question, three very different workloads. Watch the numbers, not the labels.

Fifteen thousand documents

Runbooks and postmortems in a repo. You want to ask a plain question and get the right section back.

The numbers. About fifteen thousand chunks. A few queries a minute. Content changes when someone merges a pull request.

Do the maths. Fifteen thousand vectors at 1,536 dimensions is roughly 90MB. That fits in memory without anyone noticing. HNSW answers in single digit milliseconds.

The answer. Postgres. Not as a compromise. A second database here means one more thing to back up, monitor, and keep in sync, and you would get nothing measurable back.

Do this instead. Chunk on headings, not on character counts. Store the file path and heading as columns. Then answers can cite a source, and you can search one folder at a time.

WHERE document_id LIKE 'runbooks/%'

There is your folder filter. One line, because the path is just a column.

Four million tickets

Old support tickets, growing daily. You want to find similar past incidents when a new one lands.

The numbers. Four million vectors. Tens of thousands added weekly. Every query filtered by product area, usually by date too.

Do the maths. Four million vectors at 1,536 dimensions is roughly 24GB. Memory is now a real number you have to think about. This is also where smaller embeddings start to pay for themselves.

The answer. Still Postgres, though it is closer now. The heavy filtering keeps it there. Product area and date are columns, so the planner can combine them with the vector search in one pass.

SET hnsw.iterative_scan = relaxed_order;

SELECT ticket_id, resolved_at, summary
FROM tickets
WHERE product_area = 'ingress'
  AND created_at > now() - interval '18 months'
ORDER BY embedding <=> $1
LIMIT 10;

What would change the answer. Hundreds of thousands of new rows an hour, where index maintenance becomes the bottleneck. Or growth past the point where the index stops fitting in memory. Watch for both. Do not plan for them yet.

Fifty million vectors, two hundred tenants

One search service, many teams, strict isolation between them.

The numbers. Fifty million vectors. Two hundred tenants, a few much larger than the rest. No query may ever cross tenants.

Do the maths. Here instinct and arithmetic disagree. Fifty million vectors is not a fifty million vector problem when every query is scoped to one tenant. Filter to a single tenant and you are searching two hundred thousand rows. That is small.

Measure your biggest tenant, not your total. That one reframe sends more teams back to Postgres than any benchmark.

The answer. It depends on which constraint hurts more, and pretending otherwise would be dishonest. If isolation is the hard requirement, row level security is a strong reason to stay put.

ALTER TABLE chunks ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON chunks
    USING (tenant_id = current_setting('app.tenant_id')::uuid);

With that in place, a query that forgets its tenant filter returns nothing. It does not return someone else's data. That is the difference between a bug and an incident.

If instead one noisy tenant must never slow another, a dedicated store starts to earn its keep.

ExampleSizeWhat decides itAnswer
Internal docs 15,000 chunks Nothing is under strain Postgres, easily
Support tickets 4 million Heavy filtering Postgres, with iterative scan
Shared service 50 million, 200 tenants Isolation or noisy neighbours Depends, so measure per tenant

In the wild

Notice what decided all three. It was never the vector count. It was filtering, write volume, isolation, and whether a database was already running. Picking by benchmark tunes the one thing your workload probably will not care about. That is exactly what happened to the team at the top of this post. Ask what your query really looks like, filters and all, before you ask how many vectors a product can hold.

When you do need a dedicated store

Four honest reasons.

Real scale. Tens of millions of vectors and climbing, where one Postgres box starts to strain.

Global reach. Low latency search on every continent. A purpose built distributed system does this well.

Very heavy writes. Sustained high insert rates, where index maintenance becomes the bottleneck.

Your team does not run Postgres. And does not want to start. That is a fair reason, not a technical one.

Not reasons: a benchmark, a scale you have not reached, or a diagram that looks better with another box on it.

OptionGood forThe real cost
pgvector in your Postgres Most teams, most workloads Write ceiling, and you run it
Chroma or FAISS Prototypes, one machine No sharing, no scaling out
Dedicated vector database Tens of millions, heavy filters Another critical system to run
Managed service Shipping fast Per query cost, data leaves home

Moving between these is easier than it sounds. Your chunks, their metadata, and their embeddings are the real assets. The index is derived, so you can rebuild it anywhere in an afternoon.

Build the pipeline around it

The database is one piece. Chunking and search strategy decide more of your result than the store does. The Fundamentals of RAG course on KodeKloud covers architecture, document processing and chunking, keyword against semantic search, vector databases, and building a full pipeline.

Course

Fundamentals of RAG

Architecture, document processing and chunking, keyword against semantic search, vector databases, and building a full pipeline.

RAGAIDevOps
Explore the RAG course →

Try it on your own documents

Give this an afternoon. Every step runs on a database you already have.

Take one folder of real docs. Split them on headings, not character counts. Embed the chunks and write down which model you used, because that is the choice you cannot casually undo.

Load them into a table with the vector column plus two or three plain columns: file path, heading, date. Query it with no index first. A full scan is exact, so those results become your baseline.

Now write twenty questions you would genuinely ask. Note which chunk should answer each one. Run them, and count how often the right chunk lands in the top five.

That number is your search quality. It is the only measurement here that predicts whether the thing will be useful.

Add the HNSW index and run the same twenty. Compare. You have now measured recall on your own data, which is what every benchmark you have read was only guessing at.

Finally, add a filter to one query. Restrict by date, or by folder. For most real apps, that single filter improves answers more than any index tuning will.

What you should be able to answer now

How many vectors will you really have next year? Most people guess an order of magnitude high. The honest number usually settles the whole question.

Do your queries need filters, and on what? Version, tenant, date, document type. If yes, that matters more than raw speed, and it favours a database that already understands columns.

How good is your search on twenty real questions? If you cannot say, you do not yet know whether your system works. No database choice will tell you.

Which embedding model are you tied to? Changing it means re embedding everything. Worth knowing the size of that job now, while your corpus is still small.

The team at the top of this post did careful work. They compared products thoroughly on the one dimension the industry advertises loudest, and their workload was never going to be limited by it. Twenty real questions against their eleven thousand documents would have told them, in one afternoon, that chunking was the interesting problem and the database was not a decision at all.

Ready to Build With Vectors Properly?

Vector search works when the pieces around it work. The Vector Database for GenAI course on KodeKloud covers foundations, embeddings, similarity, and internals with hands on labs. The Fundamentals of RAG course covers the chunking and search choices that decide your results. And the KodeKloud playgrounds give you somewhere to load your own docs and measure what comes back. Start with twenty questions.

Playgrounds

KodeKloud Playgrounds

Somewhere to load your own documents, index them, and measure what comes back against twenty questions you wrote yourself.

AIPythonCloud
Launch a playground →

FAQs

Q1: What is a vector database in simple terms?

It stores embeddings and finds the ones closest to your query. An embedding is a list of numbers that captures meaning, so text meaning similar things produces similar numbers. Distance between numbers becomes distance between meanings. That is what lets a search for "service will not start" return a doc titled "crash loop troubleshooting" when the two share no words. Keyword search cannot do that, and it is the whole reason these databases exist. The second thing to know is that answers are approximate on purpose. Comparing your query against every stored vector is exact, and far too slow once you pass a few tens of thousands of items. So the index skips most of them and sometimes misses a true best match. We call that recall, and production systems usually land between 90 and 99 percent. Invisible for search. Unacceptable for anything that must be complete.

Q2: Do I need a dedicated vector database, or is Postgres enough?

For most teams in 2026, Postgres is enough. That is a real change from a few years ago. The pgvector extension adds a vector column type and two index types. It handles millions of embeddings right beside your normal data, with the joins, filters, and transactions a separate store makes you rebuild yourself. Four things justify something dedicated. Real scale, meaning tens of millions of vectors and climbing. Low latency search across continents. Sustained very heavy writes, where index maintenance becomes the bottleneck. And the plain fact that your team does not run Postgres and does not want to, which is fair. What does not justify one is a benchmark measuring a scale you have not reached. Moving later is easier than people expect, because your chunks, metadata, and embeddings are the durable part. The index is derived and rebuilds anywhere.

Q3: What is the difference between HNSW and IVFFlat?

HNSW builds a layered map. The sparse top layer lets a search jump across the space, and each denser layer narrows it down, rather like finding a house by city, then street, then number. IVFFlat splits vectors into clusters and searches only the nearest few. In practice HNSW gives better accuracy and lower latency, handles updates well, and builds on an empty table, which matters for deployment pipelines. IVFFlat builds faster, uses less memory, and needs real data present first, because the clusters come from the data. For 2026, default to HNSW. Reach for IVFFlat only with a measured reason, usually very high insert rates or tight memory. Two HNSW settings matter, m and ef_construction, and 16 and 64 are sensible starting points. Doubling either buys a little accuracy and roughly doubles build time and memory, so measure before touching them.

Q4: What do I need to know before building with one?

Less than the jargon suggests, and none of it is maths. Know that an embedding is a list of numbers where similar meaning gives similar numbers. Know that the model picks how many numbers, not you. Know that search is approximate, and recall measures the trade. Know that you must use the same embedding model for storing and searching, because vectors from different models are not comparable, and mixing them looks like a broken search rather than a config error. And know that changing that model later means re embedding everything, which makes it a bigger decision than picking a database. On the practical side, if you can write SQL and call an HTTP API, you have what you need. For structured practice, the Vector Database for GenAI course on KodeKloud covers foundations, embeddings, similarity, and internals with hands on labs.

Q5: Why do my searches return relevant but wrong results?

Usually because a filter is missing, not because the search is bad. The classic case is a bot answering about the wrong product version. Docs for version 2 and version 3 describe the same features in nearly identical language, so they sit right next to each other in number space. Retrieval is working exactly as designed. Both passages genuinely are relevant to the question as asked. Better search will not fix it, and neither will reranking, because the question implied a limit the search never applied. The fix is filtering by metadata before similarity is calculated, which means storing version, date, type, or tenant as ordinary columns and using them in the query. This is the strongest practical argument for keeping vectors in a general purpose database, where those attributes are simply columns. One catch: filters and approximate search interact awkwardly, so ask for five, filter, and you may get two. Check your store handles that well.

Q6: How do I know whether my vector search is working?

Write twenty real questions and note the chunk that should answer each. Then measure how often that chunk appears in your top five. That single number predicts usefulness better than any benchmark, and it takes an afternoon to produce. Two things make it sharper. Run the same questions against an unindexed table first, because a full scan is exact by definition and gives you a correct baseline. Comparing your indexed results against it measures recall on your data rather than on someone else's public dataset. Also include questions your documents genuinely cannot answer, and check the system returns nothing convincing rather than the closest thing it found. That last behaviour surprises people. A vector search always hands back its nearest neighbours whether or not any are relevant, because there is no natural idea of "no results" in similarity search unless you set a distance threshold yourself.

Pramodh Kumar M Pramodh Kumar M

Subscribe to Newsletter

Join me on this exciting journey as we explore the boundless world of web design together.