Graph: relationships as the primary thing
A graph model stores nodes and edges, and both can carry properties. The edges aren't derived from matching values, as a foreign key is. They're stored objects, and traversing one is following a pointer.
The difference shows up in queries about *paths*. "Who reports to Ada, right or indirectly, to any depth" is one traversal in a graph and a recursive CTE in SQL — run the variant and compare it against the plain join beside it.
The gap widens with depth. Each level of a relational traversal is another join, and the planner's guesses degrade as they compound. A graph engine follows edges right, and cost scales with the number of edges really visited rather than with the size of the tables.
Where it fits. Social networks, org charts, fraud rings, dependency graphs, recommendation paths, network topology, knowledge graphs. The test is whether your notable questions are about links rather than about fields.
Where it doesn't. Aggregating over millions of rows. A graph engine is often worse at "total revenue by month" than any relational DB.
The honest comparison
| Key-value | Relational | Graph |
|---|
| Lookup by id | fastest | fast | fast |
| Ad-hoc queries | no | yes | limited |
| Joins | no | yes | native, as traversal |
| Variable-depth paths | no | recursive CTE | native |
| Aggregation | no | yes | weak |
| Schema enforcement | none | strong | usually light |
Do you need to leave SQL?
Often not, and the variants above are the arg.
A key-value table in Postgres. A main key and a jsonb column — gets you most of the model with transactions and the option of querying the value later. Redis wins on raw latency. If that isn't the binding constraint, the simpler operational story often is.
Recursive CTEs handle graph traversal, and for a few hundred thousand edges at modest depth they're just right good. Dedicated graph engines start winning at deep traversals over large graphs, and at query *expressibility* — Cypher and Gremlin say in one line what a recursive CTE says in fifteen.
The strong arg for a separate store is when the workload is overwhelmingly of one shape. The weak arg is that the model is fashionable.
Why traversal depth is where graphs win
The graph edge isn't vague. It scales with depth, and the math shows where the crossover is. "Friends of friends of friends" over a social graph with an mean of 100 links per person touches, level by level, roughly 100 then 10,000 then 1,000,000 people. A relational engine reaches each level with another join, and the planner's row guesses degrade as they times:
| depth | people reached (branching ~100) | relational cost |
|---|
| 1 | 100 | one join |
| 2 | 10,000 | join of a join |
| 3 | 1,000,000 | three joins, estimates compounding |
A graph engine follows stored edges right. So Its cost tracks *edges really visited* rather than the product of table sizes, and it doesn't re-guess a join at each level. At depth one or two a recursive CTE is just right good. By depth three or four over a large graph the relational plan is doing work proportional to the tables while the graph is doing work proportional to the answer. That divergence — not lookup speed — is the real reason to reach for a graph.
The lookup, and why it is O(1)
The reason a key-value store outruns a general DB on its one job is the lack of a tree. A relational main-key lookup descends a B-tree — about log2(n) steps — while a hash-based key-value get finds one hash and does one read, even so of size:
| keys stored | B-tree lookup | hash lookup |
|---|
| 1,000,000 | ~20 steps | ~1 probe |
| 1,000,000,000 | ~30 steps | ~1 probe |
The B-tree's cost grows, slowly, forever. The hash's doesn't grow at all. That flat line is the whole value proposition, and it's bought by giving up all a tree enables — sorted scans, range queries, "the next key after this one" — none of which a hash can answer. So the choice isn't "key-value is faster" but "key-value is faster at point lookups and can't do ranges at all", which is just the trade to weigh before moving a workload off SQL for speed.
Questions people ask about Key-Value and Graph Models
What is a key-value store good for? Fast lookups by a known key, such as sessions, caches and counters. It does one thing and does it quickly.
Why is a key-value lookup O(1)? The key is hashed to find where the value is stored, so lookup time does not grow with the number of entries.
What is a graph database? A database that stores nodes and the relationships between them, and makes following those links a primary operation.
When does a graph beat SQL joins? When queries follow connections several hops deep, such as friends of friends. Each hop in SQL adds a join, while a graph follows stored links.
Can I query a key-value store by value? Not directly. You can only look up by key unless the store adds secondary indexes, so design keys around your queries.
Do I need to leave SQL for these workloads? Often not. Recursive CTEs handle many graph questions, and a table with a key column works as a simple key-value store. Switch when scale or traversal depth demands it.
Where it goes wrong with Key-Value and Graph Models
Using a key-value store as the system of record without a plan for secondary access. The second access path is your problem, forever.
given a graph DB is faster at all. It's faster at traversals and often slower at aggregates.
Recursive CTEs without a depth limit. A cycle in the data is an infinite loop. The variant here carries a guard for just that reason.
Modelling all as a graph. If the questions are about fields rather than links, a table is the better graph.