Choosing the Right Database (or Databases): A Practical Guide

There’s rarely one “correct” database there’s a best fit per workload, and most real systems end up running more than one. This post covers the major database categories, the criteria that actually matter for choosing between them, and when polyglot persistence is worth the added operational complexity.

The Question Behind the Question

“Which database should I use” is almost always really “which database fits this specific access pattern, this specific consistency requirement, and this specific scale.” A relational database and a document store aren’t competing for the same job they’re built around different assumptions about how data is shaped and how it’s going to be queried. The mistake isn’t picking the “wrong” database in some abstract sense; it’s picking one database and forcing every workload in your system through it because switching felt like unnecessary complexity.

Start With the Access Pattern, Not the Brand Name

Before comparing Postgres to MongoDB to Redis, answer these questions honestly:

  • What does a typical read look like? A single record by ID? A complex join across five tables? A full-text search across millions of documents? A range query over time?
  • What does a typical write look like? A single row insert? A high-frequency stream of small events? A large batch load?
  • How strict does consistency need to be? Does a read need to reflect the very latest write immediately (strong consistency), or is a short delay acceptable (eventual consistency)?
  • How will the data’s shape change over time? Is the schema stable and well-understood, or will it evolve rapidly as the product changes?

The answers to these four questions narrow the field dramatically before you even start comparing specific products.

The Major Categories, and What They’re Actually For

Relational (Postgres, MySQL) The default for a reason: strong consistency, mature transactional guarantees, and a query language (SQL) that handles complex relationships and joins extremely well. Best fit when your data has clear structure, relationships between entities matter (orders relate to customers relate to products), and you need transactional integrity money moving, inventory counts, anything where “partially applied” is unacceptable.

Document stores (MongoDB, Couchbase) Best fit when your data is naturally hierarchical or varies in shape between records, and you don’t need complex cross-entity joins. A product catalog with wildly different attributes per category, or a content management system with flexible document structures, often fits this model more naturally than forcing everything into rigid relational tables.

Key-value stores (Redis, DynamoDB, Memcached) Best fit for extremely fast lookups by a known key: session data, caching layers, rate-limiting counters, feature flags. These are rarely your primary data store for complex application data they’re the layer that makes everything else feel fast.

Wide-column stores (Cassandra, HBase, Bigtable) Built for massive write throughput and horizontal scale across many nodes, at the cost of more limited query flexibility. Good fit for time-series-like data at very large scale IoT sensor readings, event logs, metrics where you mostly write and query by a known partition key, and complex ad-hoc queries aren’t the priority.

Time-series databases (TimescaleDB, InfluxDB) Purpose-built for data that’s fundamentally a sequence of timestamped measurements server metrics, financial tick data, sensor readings, vitals monitoring. These databases are optimized specifically for time-range queries, downsampling, and retention policies in ways general-purpose databases aren’t.

Graph databases (Neo4j, Amazon Neptune) Best fit when relationships between entities are the primary thing you’re querying, not just a side attribute social networks, recommendation engines, fraud detection based on connection patterns. A query like “find all people two connections away from this person who also know someone in common” is natural in a graph database and painful as a series of relational joins.

Search engines (Elasticsearch, Meilisearch, Typesense) Not a general-purpose database, but essential when full-text search, relevance ranking, and faceted filtering are core to the product experience. Most teams run this alongside a primary database, syncing relevant data into the search index rather than trying to make a relational database do full-text search well.

Criteria That Actually Drive the Decision

  1. Consistency requirements. If a stale read could mean double-spending money or overselling inventory, you need strong consistency this points hard toward relational databases or systems explicitly designed for strong consistency guarantees. If eventual consistency is genuinely fine (a “likes” counter that’s a few seconds behind), you have far more flexibility, including systems optimized purely for speed and scale over strict consistency.
  2. Query complexity and flexibility. If your access patterns are well-known and simple (get by ID, get by a fixed set of filters), simpler stores like key-value or wide-column databases can outperform a relational database at scale. If your access patterns are varied and evolving ad-hoc reporting, complex joins you haven’t anticipated yet a relational database’s flexibility is worth the trade-off even at some performance cost.
  3. Scale and growth trajectory. Most products don’t need “web scale” on day one, and optimizing for hypothetical future scale often costs real velocity now. Start with the database that fits today’s actual data volume and access patterns, and plan a migration path for the specific bottleneck you’ll hit later don’t pre-pay that complexity cost before you need it.
  4. Team familiarity and operational maturity. A team deeply experienced with Postgres will operate it more reliably at 2 a.m. during an incident than a team using their first Cassandra cluster in production, even if Cassandra is theoretically the better fit for the workload. Operational familiarity is a real, load-bearing part of the decision, not a secondary concern.
  5. Ecosystem and tooling maturity. Backup tooling, monitoring integrations, migration tooling, and the availability of experienced engineers in the hiring market all vary significantly between databases factor this in the same way you would for a backend framework choice.

When Polyglot Persistence Is Worth It

Most non-trivial systems eventually end up running more than one database, and this is normal, not a sign of over-engineering as long as each one is solving a problem the others genuinely don’t solve well:

  • Postgres as the system of record for transactional data (orders, accounts, inventory)
  • Redis as a caching and session layer sitting in front of it
  • Elasticsearch for full-text search across product listings
  • A time-series database for metrics and monitoring data

The trade-off is real: every additional database is another system to operate, monitor, back up, and keep your team skilled in. Polyglot persistence earns its complexity when a single access pattern is genuinely underserved by your primary database not because a specific technology looked interesting in a conference talk.

A Practical Decision Framework

  1. Map your actual access patterns first reads, writes, consistency needs before looking at any specific product.
  2. Default to a relational database for your system of record unless you have a clear, specific reason not to; it remains the most flexible, well-understood choice for data with real structure and relationships.
  3. Add a specialized store only when a specific access pattern is clearly underserved full-text search, sub-millisecond key lookups, massive time-series ingestion not preemptively.
  4. Weigh your team’s operational experience honestly against the theoretical performance benefit of an unfamiliar system.
  5. Plan your migration path for scale you don’t have yet, rather than architecting for it prematurely most systems can go a long way on a well-indexed relational database plus a cache before genuinely needing anything more exotic.

The Mistake to Avoid

The two most common expensive mistakes are mirror images of each other: forcing every workload through one database because introducing a second one feels like unnecessary complexity, and adopting three specialized databases on day one because a scaling story from a much larger company made it feel like the responsible choice. Neither is really a technical decision both are decisions made to avoid discomfort, one by avoiding new tooling, the other by avoiding the appearance of being under-engineered. The right call is almost always simpler than either extreme: start with what your actual access patterns need today, and add specialization only when a real, measured bottleneck justifies it.