Which database should I use? A decision tree for your app

Choose a database for a new app: SQLite, PostgreSQL or MySQL, a document store, Redis, graph, time-series, search or vector store, or Postgres plus extension.

Which database should I use? A decision tree for your appWHERE THE DATA LIVESWHO RUNS ITDATA SHAPESPECIAL WORKLOADSNo, one machineYesNo ops staffYour own teamRelated tablesYesNo or unsureDocumentsYesRarelyOftenSomething more specialisedYesNoYesYes, many hopsYesNoYesNoYesNoNoYesNot sureNoKeys and valuesConnected entitiesTime-stamped eventsText to searchEmbeddingsNone or not sureNoOne or two hopsChoose a database for yourapplicationFor developers starting an app:answer questions on where datalives, its shape and its consistencyneeds, and get a database type touse.General practice, vendor-neutral.Most apps do well on one relationaldatabase. Add a specialised storeonly when a need below is real andmeasured.Before you start, list the 5 to 10queries your app runs most, the datasize you expect in a year, and howmany processes write at once.Will several machines writeto the database over anetwork?SQLite's own checklist asks threethings: is the data separated fromthe app by a network, are there manyconcurrent writers, and is the dataapproaching a terabyte?Will the data staywell under aterabyte?Use SQLite, embedded inyour appSQLite is a library, not a server. Itsuits phones, desktop apps, devices,tests, data analysis and many smallor medium websites. sqlite.org saysunder 100K hits a day is aconservative figure.It allows unlimited readers but onlyone writer at any instant. WAL modelets readers and the writer run at thesame time.Check: WAL mode needs allprocesses on the same host, not anetwork filesystem. If you later needseveral app servers, plan a move to aclient/server database.Who will look afterthe database day today?Plan on a managed servicefor any server databaseA managed service runs backups,patching and failover. Check thebackup retention period and how farback point-in-time recovery reaches.Plan backups, upgrades andfailover yourselvesTest a full restore before launch.Copy backups off the machine, andoutside the data centre, at least daily.What shape is most of yourdata?Pick the shape of your core records,the ones you can't lose. Caches,search and analytics can be addedlater.Does your team already runMySQL well?Related tables means records thatpoint to each other: customers,orders and invoices, with joins andtransactions across them.Use MySQL with the InnoDBengineInnoDB is MySQL's default engine. Itis ACID-compliant with row-levellocking, foreign keys and crashrecovery.Check: the MySQL manual lists a64TB limit per table or tablespace atthe default 16KB page size, and yourfile system may set a lower one.Check which features your MySQLversion supports, such as JSON andfull-text indexes.Use PostgreSQL as yourmain databaseACID-compliant, supports all SQLstandard isolation levels includingSerializable, and handlesJSON/JSONB, arrays, ranges andfull-text search.Extensions such as PostGIS (maps),TimescaleDB (time series) andpgvector (embeddings) let onedatabase cover several needs.Check: plan connection pooling early.Each Postgres connection is a serverprocess, so many short-livedconnections need a pooler.Do records vary in shape andget read whole?Documents are self-containedrecords, like a product with itsvariants and specs, where fieldsdiffer from one record to the next.Do you often needto change severaldocuments in onetransaction?Use a document store suchas MongoDBStore data that is read together inone document. A write to a singledocument is atomic.Multi-document transactions exist,but MongoDB's docs say they costmore than single-document writesand are no substitute for goodschema design.Check: add schema validation to thefields that need it. A flexible schemacan still drift into inconsistent data.Use PostgreSQL with JSONBcolumnsKeep the fixed fields as columns andthe variable part in a JSONB column.You keep joins and multi-rowtransactions.Check: index the JSON keys you filteron, or queries will scan every row.Which of thesedescribes the coreworkload?Is this data a cache youcould rebuild fromsomewhere else?Sessions, rate-limit counters,leaderboards and cached queryresults are typical key-valueworkloads.Use a key-value cache suchas RedisRedis keeps data in memory, soreads and writes are very fast.Persistence can be turned off for apure cache.Check: set an eviction policy and amemory limit, and make your appwork, slowly, when the cache isempty.Can you accept losing up tothe last second of writes?Redis with append-only file (AOF)persistence and the default fsyncevery second can lose about onesecond of writes in a crash.Snapshots alone can lose minutes.Use Redis with AOF and RDBpersistenceRedis docs suggest using bothmethods if you want data safetycomparable to PostgreSQL.Check: copy RDB snapshots off themachine for backups, and sizememory for the whole dataset.Do key queries follow manyhops of relationships?For example, friends of friends, fraudrings, supply chains or permissioninheritance, where the depth of thepath is not fixed.Use a graph database suchas Neo4jGraph databases store nodes,relationships and properties, andtraverse relationships without joinoperations.Check: graph databases are rarelythe system of record for billing ororders. Many teams keep those in arelational database and sync a graph.Already using PostgreSQLfor the rest of the app?Time-series data is metrics, sensorreadings, prices and logs that arrivein time order and are queried by timerange.Use PostgreSQL with theTimescaleDB extensionHypertables split data into chunks bytime range, such as one day or oneweek, while you keep using SQL andjoins.Check: set a retention policy early.Time-series data grows without limit.Use a dedicated time-seriesdatabaseBuilt for high write rates,time-bucketed queries andautomatic expiry of old data.Check: confirm how it handlesupdates, deletes and joins. Many arebuilt mainly for appends, so keepcustomer and billing records in yourmain database.Need typo tolerance, facetsor search across manysources?Full-text search means findingdocuments by words and rankingthem by relevance, not exactmatches.Use a search engine such asElasticsearch or OpenSearchBuilt for relevance ranking, fuzzymatching, facets and aggregations atlarge scale.Check: it is near real-time, so newdocuments usually becomesearchable within about 1 second.Keep the source data in your maindatabase and reindex from it.Use PostgreSQL full-textsearchPostgres full-text search addsstemming, stop words, ranking andGIN indexes that plain LIKE querieslack.Check: it matches word stems, nottypos. Add the pg_trgm extension ifyou need fuzzy matching.Can the vectors live next toyour PostgreSQL data?Embeddings are lists of numbersfrom a machine-learning model,searched by similarity. pgvectorindexes up to 2,000 dimensions, or4,000 at half precision.Use PostgreSQL with thepgvector extensionYou keep ACID transactions, joins,point-in-time recovery and filters onordinary columns in the same query.Check: approximate indexes (HNSW,IVFFlat) trade recall for speed.Measure recall on your own dataafter you add one.Use a dedicated vectordatabaseFits very large vector collections, orteams not running PostgreSQL.Check: you now sync two systems.Plan how deletes and permissionchanges reach the vector store.

Where the data lives

  1. Choose a database for your application

    For developers starting an app: answer questions on where data lives, its shape and its consistency needs, and get a database type to use.

    General practice, vendor-neutral. Most apps do well on one relational database. Add a specialised store only when a need below is real and measured.

    Before you start, list the 5 to 10 queries your app runs most, the data size you expect in a year, and how many processes write at once.

  2. Will several machines write to the database over a network?

    SQLite's own checklist asks three things: is the data separated from the app by a network, are there many concurrent writers, and is the data approaching a terabyte?

  3. Will the data stay well under a terabyte?
  4. Use SQLite, embedded in your app

    SQLite is a library, not a server. It suits phones, desktop apps, devices, tests, data analysis and many small or medium websites. sqlite.org says under 100K hits a day is a conservative figure.

    It allows unlimited readers but only one writer at any instant. WAL mode lets readers and the writer run at the same time.

    Check: WAL mode needs all processes on the same host, not a network filesystem. If you later need several app servers, plan a move to a client/server database.

Who runs it

  1. Who will look after the database day to day?
  2. Plan on a managed service for any server database

    A managed service runs backups, patching and failover. Check the backup retention period and how far back point-in-time recovery reaches.

    Then go to step 8, What shape is most of your data?

  3. Plan backups, upgrades and failover yourselves

    Test a full restore before launch. Copy backups off the machine, and outside the data centre, at least daily.

Data shape

  1. What shape is most of your data?

    Pick the shape of your core records, the ones you can't lose. Caches, search and analytics can be added later.

  2. Does your team already run MySQL well?

    Related tables means records that point to each other: customers, orders and invoices, with joins and transactions across them.

  3. Use MySQL with the InnoDB engine

    InnoDB is MySQL's default engine. It is ACID-compliant with row-level locking, foreign keys and crash recovery.

    Check: the MySQL manual lists a 64TB limit per table or tablespace at the default 16KB page size, and your file system may set a lower one. Check which features your MySQL version supports, such as JSON and full-text indexes.

  4. Use PostgreSQL as your main database

    ACID-compliant, supports all SQL standard isolation levels including Serializable, and handles JSON/JSONB, arrays, ranges and full-text search.

    Extensions such as PostGIS (maps), TimescaleDB (time series) and pgvector (embeddings) let one database cover several needs.

    Check: plan connection pooling early. Each Postgres connection is a server process, so many short-lived connections need a pooler.

  5. Do records vary in shape and get read whole?

    Documents are self-contained records, like a product with its variants and specs, where fields differ from one record to the next.

  6. Do you often need to change several documents in one transaction?
  7. Use a document store such as MongoDB

    Store data that is read together in one document. A write to a single document is atomic.

    Multi-document transactions exist, but MongoDB's docs say they cost more than single-document writes and are no substitute for good schema design.

    Check: add schema validation to the fields that need it. A flexible schema can still drift into inconsistent data.

  8. Use PostgreSQL with JSONB columns

    Keep the fixed fields as columns and the variable part in a JSONB column. You keep joins and multi-row transactions.

    Check: index the JSON keys you filter on, or queries will scan every row.

  9. Which of these describes the core workload?

Special workloads

  1. Is this data a cache you could rebuild from somewhere else?

    Sessions, rate-limit counters, leaderboards and cached query results are typical key-value workloads.

  2. Use a key-value cache such as Redis

    Redis keeps data in memory, so reads and writes are very fast. Persistence can be turned off for a pure cache.

    Check: set an eviction policy and a memory limit, and make your app work, slowly, when the cache is empty.

  3. Can you accept losing up to the last second of writes?

    Redis with append-only file (AOF) persistence and the default fsync every second can lose about one second of writes in a crash. Snapshots alone can lose minutes.

  4. Use Redis with AOF and RDB persistence

    Redis docs suggest using both methods if you want data safety comparable to PostgreSQL.

    Check: copy RDB snapshots off the machine for backups, and size memory for the whole dataset.

  5. Do key queries follow many hops of relationships?

    For example, friends of friends, fraud rings, supply chains or permission inheritance, where the depth of the path is not fixed.

  6. Use a graph database such as Neo4j

    Graph databases store nodes, relationships and properties, and traverse relationships without join operations.

    Check: graph databases are rarely the system of record for billing or orders. Many teams keep those in a relational database and sync a graph.

  7. Already using PostgreSQL for the rest of the app?

    Time-series data is metrics, sensor readings, prices and logs that arrive in time order and are queried by time range.

  8. Use PostgreSQL with the TimescaleDB extension

    Hypertables split data into chunks by time range, such as one day or one week, while you keep using SQL and joins.

    Check: set a retention policy early. Time-series data grows without limit.

  9. Use a dedicated time-series database

    Built for high write rates, time-bucketed queries and automatic expiry of old data.

    Check: confirm how it handles updates, deletes and joins. Many are built mainly for appends, so keep customer and billing records in your main database.

  10. Need typo tolerance, facets or search across many sources?

    Full-text search means finding documents by words and ranking them by relevance, not exact matches.

  11. Use a search engine such as Elasticsearch or OpenSearch

    Built for relevance ranking, fuzzy matching, facets and aggregations at large scale.

    Check: it is near real-time, so new documents usually become searchable within about 1 second. Keep the source data in your main database and reindex from it.

  12. Can the vectors live next to your PostgreSQL data?

    Embeddings are lists of numbers from a machine-learning model, searched by similarity. pgvector indexes up to 2,000 dimensions, or 4,000 at half precision.

  13. Use PostgreSQL with the pgvector extension

    You keep ACID transactions, joins, point-in-time recovery and filters on ordinary columns in the same query.

    Check: approximate indexes (HNSW, IVFFlat) trade recall for speed. Measure recall on your own data after you add one.

  14. Use a dedicated vector database

    Fits very large vector collections, or teams not running PostgreSQL.

    Check: you now sync two systems. Plan how deletes and permission changes reach the vector store.

Outcomes

Use SQLite, embedded in your app

SQLite is a library, not a server. It suits phones, desktop apps, devices, tests, data analysis and many small or medium websites. sqlite.org says under 100K hits a day is a conservative figure.

It allows unlimited readers but only one writer at any instant. WAL mode lets readers and the writer run at the same time.

Check: WAL mode needs all processes on the same host, not a network filesystem. If you later need several app servers, plan a move to a client/server database.

You get here from step 3, Will the data stay well under a terabyte? (Yes).

Use MySQL with the InnoDB engine

InnoDB is MySQL's default engine. It is ACID-compliant with row-level locking, foreign keys and crash recovery.

Check: the MySQL manual lists a 64TB limit per table or tablespace at the default 16KB page size, and your file system may set a lower one. Check which features your MySQL version supports, such as JSON and full-text indexes.

You get here from step 9, Does your team already run MySQL well? (Yes).

Use PostgreSQL as your main database

ACID-compliant, supports all SQL standard isolation levels including Serializable, and handles JSON/JSONB, arrays, ranges and full-text search.

Extensions such as PostGIS (maps), TimescaleDB (time series) and pgvector (embeddings) let one database cover several needs.

Check: plan connection pooling early. Each Postgres connection is a server process, so many short-lived connections need a pooler.

You get here from step 9, Does your team already run MySQL well? (No or unsure), step 12, Do records vary in shape and get read whole? (No), step 16, Which of these describes the core workload? (None or not sure), step 19, Can you accept losing up to the last second of writes? (No), step 21, Do key queries follow many hops of relationships? (One or two hops).

Use a document store such as MongoDB

Store data that is read together in one document. A write to a single document is atomic.

Multi-document transactions exist, but MongoDB's docs say they cost more than single-document writes and are no substitute for good schema design.

Check: add schema validation to the fields that need it. A flexible schema can still drift into inconsistent data.

You get here from step 13, Do you often need to change several documents in one transaction? (Rarely).

Use PostgreSQL with JSONB columns

Keep the fixed fields as columns and the variable part in a JSONB column. You keep joins and multi-row transactions.

Check: index the JSON keys you filter on, or queries will scan every row.

You get here from step 13, Do you often need to change several documents in one transaction? (Often).

Use a key-value cache such as Redis

Redis keeps data in memory, so reads and writes are very fast. Persistence can be turned off for a pure cache.

Check: set an eviction policy and a memory limit, and make your app work, slowly, when the cache is empty.

You get here from step 17, Is this data a cache you could rebuild from somewhere else? (Yes).

Use Redis with AOF and RDB persistence

Redis docs suggest using both methods if you want data safety comparable to PostgreSQL.

Check: copy RDB snapshots off the machine for backups, and size memory for the whole dataset.

You get here from step 19, Can you accept losing up to the last second of writes? (Yes).

Use a graph database such as Neo4j

Graph databases store nodes, relationships and properties, and traverse relationships without join operations.

Check: graph databases are rarely the system of record for billing or orders. Many teams keep those in a relational database and sync a graph.

You get here from step 21, Do key queries follow many hops of relationships? (Yes, many hops).

Use PostgreSQL with the TimescaleDB extension

Hypertables split data into chunks by time range, such as one day or one week, while you keep using SQL and joins.

Check: set a retention policy early. Time-series data grows without limit.

You get here from step 23, Already using PostgreSQL for the rest of the app? (Yes).

Use a dedicated time-series database

Built for high write rates, time-bucketed queries and automatic expiry of old data.

Check: confirm how it handles updates, deletes and joins. Many are built mainly for appends, so keep customer and billing records in your main database.

You get here from step 23, Already using PostgreSQL for the rest of the app? (No).

Use a search engine such as Elasticsearch or OpenSearch

Built for relevance ranking, fuzzy matching, facets and aggregations at large scale.

Check: it is near real-time, so new documents usually become searchable within about 1 second. Keep the source data in your main database and reindex from it.

You get here from step 26, Need typo tolerance, facets or search across many sources? (Yes).

Use PostgreSQL with the pgvector extension

You keep ACID transactions, joins, point-in-time recovery and filters on ordinary columns in the same query.

Check: approximate indexes (HNSW, IVFFlat) trade recall for speed. Measure recall on your own data after you add one.

You get here from step 29, Can the vectors live next to your PostgreSQL data? (Yes).

Use a dedicated vector database

Fits very large vector collections, or teams not running PostgreSQL.

Check: you now sync two systems. Plan how deletes and permission changes reach the vector store.

You get here from step 29, Can the vectors live next to your PostgreSQL data? (No).