How to choose a database
In short: Choose a database by your data and the questions you ask of it: a relational database holds the truth, and another kind is added next to it only where it is weak.
Problem
Many kinds of databases on the table
The module showed key-value, documents, event streams, graphs, columns and time series, text search, vectors and clusters. Each of these databases is excellent at something. The shop's new project asks: which one do we choose?
The wrong way is to start from the database ("we use MongoDB because it is modern"). The right way is to start from the data and the questions you will ask about it, and to add a new database only when there is a question the main one does not answer well.
The idea
Five questions
- What shape is the data? Rows with the same columns, whole documents with nested parts, connections between things, records over time, free text.
- What do you ask? One thing by key, a report over everything, "who is connected to whom", "which words", "what is similar".
- How important is consistency? Money and stock need transactions and reading the latest write. A like counter can lag by a second.
- How big are the data and the load? Thousands or billions of rows, ten or a hundred thousand queries a second. Most projects are much smaller than they think.
- Who will look after it? Every database needs copies, monitoring, upgrades and someone who knows what to do when it goes down.
| The question | Kind of database |
|---|---|
| Money, orders, stock, everything that has to be right | relational (PostgreSQL, MySQL) |
| The same thing by key thousands of times, sessions, carts | key-value (Redis) |
| Whole objects with different structures | document (MongoDB) or JSONB |
| Many systems learn about the same event | event stream (Kafka) |
| Recommendations, networks, fraud across several steps | graph (Neo4j) |
| Reports over millions of rows, sensors, metrics | columnar, time series (ClickHouse, TimescaleDB) |
| A search box, words in text | full-text search (Elasticsearch) |
| "Similar in meaning", a chatbot over documents | vector (pgvector, Qdrant) |
The idea
Start with PostgreSQL
For most new projects the sensible start is one relational database. PostgreSQL alone covers a large part of the module: JSONB columns for documents, tsvector for text search, pgvector for vectors, TimescaleDB for time series, read replicas and partitioning tables by time. It is not the best at each of them, but it is good enough, and one database is much easier to look after than five.
A separate database pays off when there is a concrete question the main one answers slowly or with difficulty, and you have measured it: the cache, because the same query runs 2000 times a minute; the search engine, because search is the main job of the site; the data warehouse, because the reports slow down the till.
More precisely, if you are curious
This is called polyglot persistence: different databases for different parts of the system. It works well when one of them is clearly the source of truth and the others are copies that can be rebuilt. It works badly when two databases hold different parts of the same truth and nobody knows which one is right.
See it
The shop when it grows
This is what the shop might look like after a few years, with each part added when it was needed:
- PostgreSQL keeps the customers, the products, the orders and the payments. This is the truth. It has one synchronous and one asynchronous replica.
- Redis keeps the catalogue cache, the sessions and the carts.
- Kafka carries the events "order placed", "paid", "shipped" to the warehouse, the invoices and the e-mail.
- Elasticsearch holds a copy of the products and reviews for the search box. It is filled from the events.
- ClickHouse is the data warehouse for the reports. It is filled every night or from the stream.
- pgvector in the same PostgreSQL gives "similar products".
All the copies come from PostgreSQL. If the index in Elasticsearch breaks, it is rebuilt from the database. The reverse is not true.
Problem
The two writes
The order is written to PostgreSQL, and then the program sends an event to Kafka. If the program crashes between the two, the order is in the database, but the event is missing: the warehouse never packs the parcel. If it is the other way round (first the event, then the database), the warehouse may pack a parcel for an order that later failed.
The two systems have no common transaction. The solution is an outbox table: the event is written to a table in the same database, in the same transaction as the order. Either both are written, or neither.
published_at is NULL: the event has not been sent yet. A separate program (a relay) reads the unsent ones in order, sends them to the stream and only then marks them:
And if something fails before COMMIT, there is neither a change nor an event:
More precisely, if you are curious
The relay can crash after it has sent the event but before it marks it. Then on restart it sends it again. That is why the consumers of the event have to be idempotent, like the invoices in the lesson on streams. Instead of a program that reads the table, Debezium is often used: it reads PostgreSQL's change log (the WAL) and sends every new row of the outbox to Kafka. This is called change data capture (CDC).
Try it
Cancelling an order
The customer cancels order 42. In one transaction change its status to cancelled and write an event to the outbox for the topic orders with the key order-42 and the content {"order":42,"event":"cancelled"}.
Solution
BEGIN;
UPDATE orders SET status = 'cancelled' WHERE id = 42;
INSERT INTO outbox (topic, key, payload)
VALUES ('orders', 'order-42', '{"order":42,"event":"cancelled"}');
COMMIT;BEGIN, UPDATE orders SET status = 'cancelled' WHERE id = 42, INSERT INTO outbox (topic, key, payload) VALUES ('orders', 'order-42', '…') and COMMIT. Only the UPDATE changes the status, but the warehouse and the e-mail never find out. The key is the order number, so that the cancellation goes into the same partition as "placed" and "paid" and is read after them.
Exercise
Five projects
For each project: which database is the foundation, or what do you add next to it?
Test
Check yourself
Related lessons
Section titled "Related lessons"- Builds on: Columnar and time series databases · Searching text · Searching by meaning · Replication, sharding and CAP
- Tasks on this topic: Kinds of databases