NoSQL Databases: Key-Value, Document, Wide-Column and Graph

Why NoSQL exists (schema rigidity, horizontal scaling), the four families with examples, when to use NoSQL and what it gives up compared with SQL.

Beginner 7 min read · Updated

Relational databases are excellent at structured data with complex relationships, but they struggle with two modern problems: schemas that keep changing and scaling writes across many machines. NoSQL ("Not Only SQL") databases give up some SQL guarantees to solve those two problems. The term covers four quite different families (key-value, document, wide-column and graph), each built around one data model and a few access patterns it serves extremely well. Knowing which family fits which problem matters more than knowing any one product.

Problem 1: schema rigidity

A social media startup keeps user profiles in PostgreSQL with a clean schema:

CREATE TABLE users (
    user_id int PRIMARY KEY,
    name    varchar(255),
    email   varchar(255),
    bio     text
);

Three months later the product team wants verified badges: add a column. Six months in, premium users get custom themes: another column. A year in, influencers need follower counts, engagement rates and top hashtags. Every feature means a migration on a 10-million-row table, each migration locks the table, and deployments take hours. A competitor on MongoDB adds fields instantly, with no migration and no downtime, and ships weekly while the SQL team ships monthly.

Rich nested data shows the same strain. An Instagram post has an image, caption, hashtags, location, timestamp and comments, and comments have authors and nested replies. In relational tables, reading one post joins Posts → Comments → Users → Locations, five tables for one screen. When the product adds "story reactions" one week and "polls" the next, every change needs coordinated migrations. A document database stores the whole post (image, caption, comments array, location object) as one document: no joins, and new fields need no migration.

{
  "post_id": "p_9001",
  "author": { "user_id": 42, "name": "Alice" },
  "image_url": "https://cdn.example.com/p_9001.jpg",
  "caption": "Sunset hike",
  "hashtags": ["hiking", "sunset"],
  "location": { "name": "Twin Peaks", "lat": 37.75, "lng": -122.45 },
  "created_at": "2026-03-14T18:22:00Z",
  "comments": [
    { "author": "bob", "text": "Wow!", "replies": [ { "author": "alice", "text": "Thanks!" } ] }
  ],
  "poll": { "question": "Next trail?", "options": ["Mt Tam", "Big Sur"] }
}

Problem 2: horizontal scaling

An e-commerce site keeps its product catalog in MySQL. Traffic grows until the database hits CPU limits. Vertical scaling buys time: 8 cores, then 16, 32, 64. Then hardware limits appear, and a 128-core server costs far more than two 64-core servers. Sharding a relational database means choosing partition keys by hand and routing in the application; joins across shards get expensive and foreign keys stop working. The team spends months on sharding logic instead of features. See scaling databases.

How NoSQL databases work

NoSQL databases store data without a fixed schema: different records can have different fields, and adding a field needs no migration. They also partition data automatically across nodes. Because a distributed system must keep working through network partitions, many NoSQL stores favor availability and offer eventual or tunable consistency rather than strong consistency by default: writes reach every replica within seconds, and strong consistency, where offered, costs some availability and latency. See CAP theorem.

Schema flexibility

Two user profiles can live side by side in the same collection, one with a badge and one without. The database accepts both; application code handles missing fields.

users collection{ user_id: 123 }name: Alice · email · bio: "Software engineer"{ user_id: 456 }name: Bob · email · verified: true · badge_color: gold
One users collection, two shapes: Bob has verified and badge_color fields that Alice doesn't. No migration was needed to add them.
{ "user_id": 123, "name": "Alice", "email": "alice@example.com", "bio": "Software engineer" }

{ "user_id": 456, "name": "Bob", "email": "bob@example.com", "verified": true, "badge_color": "gold" }

Automatic partitioning and replication

DynamoDB spreads items across servers by partition key; add capacity and it rebalances data automatically. Cassandra replicates each partition to several nodes with configurable consistency: for example, write to three replicas for durability and read from one for speed. The system stays available when individual nodes fail. See consistent hashing and quorums.

ownerreplicareplicaPUT itempartition key = user_456hash(partition key)Node 1partitions A–F + replicasNode 2partitions G–M + replicasNode 3partitions N–Z + replicas
Automatic partitioning: the partition key is hashed to pick a node, and each partition is copied to replicas so a node failure doesn't lose data or availability.

The four NoSQL families

Each family optimizes for a different access pattern. Choose by access pattern, not just by the shape of the data.

Key-value stores

The simplest model: a giant hash map from a key to a value. Operations are GET, PUT and DELETE by key, O(1) and easy to partition by hashing the key, but there are no queries across values. Full detail in key-value databases.

session:abc123    →  { "user_id": 456, "login_time": "2026-03-14T09:12:00Z", "exp": 1773480720 }
cart:u456         →  ["sku_1", "sku_9"]
rate:ip:1.2.3.4   →  27
  • Examples: Redis, Memcached (in-memory), DynamoDB, Riak, etcd (small, strongly consistent).
  • Great for: caches, sessions, feature flags, rate-limit counters, shopping carts, idempotency keys. Redis keeps session data for logged-in users; DynamoDB handles high-throughput lookups with predictable performance.
  • Limits: no queries by value; anything other than lookup by key needs a separate index you maintain.

Document stores

Values are structured JSON-like (JSON/BSON) documents that the database understands, so you can index and query fields inside them. A document is self-contained: an entity with its nested fields and arrays, matching how application code models objects. Full detail in document databases.

{ "_id": "prod_812", "name": "Trail running shoe", "brand": "Stride",
  "variants": [ { "size": 9, "color": "blue", "stock": 14 } ],
  "attributes": { "drop_mm": 6, "waterproof": false } }
  • Examples: MongoDB (product catalogs), CouchDB (content management), Couchbase, Firestore, Azure Cosmos DB, PostgreSQL jsonb.
  • Great for: catalogs with varying attributes, user profiles, content management, user-generated content, data read as a whole object.
  • Limits: joins across documents are limited or slow; data shared by many documents gets duplicated.

Wide-column (column-family) stores

Data lives in tables, but each row is identified by a partition key (which node stores it) and a clustering key (sort order within the partition). Rows in a partition are stored together, sorted, so range scans within a partition are fast, and data is organized by column families so reading specific columns is efficient while full-row scans stay expensive. They're built on LSM trees for very high write throughput.

hash(c42)range scanQueryconversation = c42, last 50Node 1partitions c17, c42Node 2partitions c08, c99c42 rows, sorted by timet1 · t2 · … · t9050
A Cassandra-style table for chat: partition by conversation, cluster by time. "Latest 50 messages in a conversation" is one sequential read on one node.
-- Cassandra CQL: one table per query
CREATE TABLE messages (
    conversation_id text,
    sent_at         timestamp,
    message_id      uuid,
    author_id       text,
    body            text,
    PRIMARY KEY ((conversation_id), sent_at, message_id)   -- partition key, then clustering keys
) WITH CLUSTERING ORDER BY (sent_at DESC, message_id DESC);

SELECT * FROM messages WHERE conversation_id = 'c42' LIMIT 50;
  • Examples: Apache Cassandra (time-series, messaging), HBase (analytics workloads), ScyllaDB, Google Bigtable.
  • Great for: time-series, messaging, activity feeds, logs, IoT and event data: huge write volumes read by key and time range.
  • Limits: you design one table per query; no joins; secondary indexes are weak. Picking the partition key is everything (see sharding).

Graph databases

Data is nodes and edges with properties, and relationships are first-class. Queries traverse relationships ("friends of friends who like this band"), which is fast because the database follows pointers directly. In a relational database each hop is a join, which gets expensive at several hops. Aggregations over the whole dataset remain slow.

FOLLOWSFOLLOWSFOLLOWSFOLLOWSLIKESAliceBobDanCarolsuggestedBand: The Lumens
A tiny social graph: "who should Alice follow?" walks Alice → Bob → Carol and Alice → Dan → Carol, two hops instead of two self-joins.
// Cypher (Neo4j): friends of friends Alice doesn't follow yet
MATCH (a:User {name: 'Alice'})-[:FOLLOWS]->(:User)-[:FOLLOWS]->(fof:User)
WHERE NOT (a)-[:FOLLOWS]->(fof) AND fof <> a
RETURN fof.name, count(*) AS mutuals ORDER BY mutuals DESC LIMIT 10;
  • Examples: Neo4j (social networks), ArangoDB (recommendation engines), Amazon Neptune, JanusGraph, TigerGraph.
  • Great for: social networks, recommendations, fraud rings (accounts sharing devices or cards), knowledge graphs, network topology.
  • Limits: harder to partition across machines; not ideal for bulk aggregation over all data.

When to use NoSQL

  • Unpredictable schemas: when features evolve fast and profile fields change weekly, document and wide-column stores absorb new fields without locking migrations on a large table.
  • High write throughput: a single database can't take millions of writes per second. Discord stores trillions of messages; Cassandra-style stores partition writes across hundreds of nodes.
  • Simple access patterns: shopping carts are key-value lookups by session ID, and session data needs no joins or transactions. Redis serves these from memory, so end-to-end latency is typically sub-millisecond (in-memory access takes microseconds; the network round trip is the rest).
  • Global distribution: social feeds tolerate seconds of staleness; nobody notices if a like count updates slightly late. DynamoDB global tables replicate across regions with configurable consistency. See geo-replication.
  • Large unstructured datasets: log aggregation can ingest gigabytes per second; Elasticsearch indexes and searches text without a fixed schema, where SQL full-text search becomes impractical. See full-text search.

What NoSQL gives up

  • Eventual consistency complicates application logic. Two users editing the same document may see different versions, and the application must resolve conflicts. Amazon's Dynamo shopping cart merged conflicting versions by keeping every item ever added, so deleted items occasionally reappeared. See conflict resolution with CRDTs.
  • No ACID transactions across records (or limited ones). A money transfer becomes two separate updates; if the system fails between them, money disappears or is duplicated. Applications fall back to sagas or accept some inconsistency.
  • Limited queries mean denormalization. Without joins, data is duplicated to avoid them: a user's name is copied into each of their posts, and a name change means updating thousands of posts. See normalization vs denormalization.
  • Operational complexity. SQL has mature tooling for backup, monitoring and debugging. NoSQL stores need new tools and skills: Cassandra's eventual consistency makes debugging harder, and DynamoDB's pricing model needs careful capacity planning.

Common implementations

ProductFamilyKnown forUse it for
MongoDBDocumentThe most popular document database; rich queries with filters, aggregations and text searchProduct catalogs, user-generated content, documents with varying schemas
RedisKey-value (in-memory)Sub-millisecond latency; lists, sets, sorted sets beyond plain values. See RedisCaching, real-time leaderboards, rate limiting, sessions
Apache CassandraWide-columnDistributed with no single point of failure; millions of writes per second across nodesTime-series data, event logging, high-write applications
Amazon DynamoDBKey-value and document (managed)Auto-scaling, single-digit millisecond latency at any scaleServerless apps, unpredictable traffic
Neo4jGraphNative graph storage and the Cypher query languageSocial graphs, recommendations, fraud detection

Choosing between them

NeedFamilyExample product
Sub-millisecond lookup by key, caching, countersKey-value (in-memory)Redis
Durable key-value at any scale, managedKey-value / wide-columnDynamoDB
Rich objects with varying fields, queried by fieldDocumentMongoDB
Massive writes, read by key and time rangeWide-columnCassandra, Bigtable
Multi-hop relationship queriesGraphNeo4j, Neptune
Full-text search and relevanceSearch engineElasticsearch. See full-text search
Metrics over time with downsamplingTime-seriesPrometheus, InfluxDB. See time-series databases

Need millions of writes per second? Cassandra. Schema changes deploying daily? MongoDB. Only simple key lookups? Redis. Otherwise a relational database is usually the simpler start; see SQL vs NoSQL.

Where it’s used

Discord

Stored trillions of messages in Cassandra (later ScyllaDB), partitioned by channel and time bucket, for fast writes and recent-history reads.

Twitter and Netflix

Use Redis and Memcached heavily as caches and for counters, timelines and sessions in front of durable stores.

eBay and product catalogs

Document stores hold listings whose attributes differ by category.

PayPal and banks

Graph analysis links accounts, devices and cards to spot fraud rings that row-by-row checks miss.

In an interview

  • Name the family before the product, and tie it to the access pattern.
  • For wide-column stores, state the partition key and clustering key for each query.
  • Say what you give up (cross-record transactions, joins, sometimes strong consistency) and how the design copes.
  • Use Redis as a cache or for counters, not as the only copy of important data unless persistence is configured and justified.
  • Reach for a graph database only when multi-hop traversals are core to the product.

Further reading