NoSQL Data Management

Exam-ready notes on NoSQL data management: types, aggregates, key-value and document models, graph databases, map-reduce, and partitioning.

Materio
Listen
0

Introduction to NoSQL

You're building a library management system. Members borrow books, books have reviews, reviews have replies, and some books are co-authored by multiple people with tangled relationships between them. You try modeling all of this in strict rows and columns and the joins start multiplying every time you add a feature. That mismatch between rigid tables and messy, connected, fast-growing real-world data is exactly what pushed engineers toward NoSQL.

NoSQL (originally "Not Only SQL") refers to a family of database systems that store and query data without relying on the fixed table-based relational model used by traditional SQL databases.

Where the Term Actually Comes From

NoSQL isn't one technology. It's an umbrella term covering databases built with different priorities than relational systems: flexible schemas, horizontal scalability, and speed at massive scale over strict consistency and rigid structure.

  • Relational databases: fixed schema, tables, strong consistency, vertical scaling
  • NoSQL databases: flexible or no schema, varied data models, horizontal scaling, tunable consistency

[!NOTE]
NoSQL doesn't mean "no SQL query language exists." Some NoSQL databases like Cassandra even offer SQL-like query languages (CQL). The name refers to the departure from the relational model, not the syntax.

Why This Shift Happened

  • Web-scale data: Companies like Amazon and Google needed to handle millions of concurrent users, something single relational servers struggled with
  • Unstructured and semi-structured data: JSON documents, logs, social graphs, and sensor data don't fit neatly into rows and columns
  • Agile development: Schemas that change every sprint clash with rigid ALTER TABLE migrations
  • Distributed systems demand: Applications needed to run across many cheap servers instead of one expensive one

MCQ

What does the "No" in NoSQL most accurately represent?

Types of NoSQL

Now that you know why NoSQL exists, the next question is: NoSQL isn't a single database, so which kind do you actually pick for your library system? Different NoSQL databases are optimized for different shapes of data, and choosing the wrong one creates the same pain you were trying to avoid.

There are four major categories of NoSQL databases, each modeling data differently based on the access patterns they're built for.

The Four Major Categories

Type Structure Example Use in Library System
Key-Value Store Simple key mapped to a value Caching a member's current session token
Document Store Self-contained JSON-like documents Storing a full book record with nested reviews
Column-Family Store Rows grouped into column families Storing loan history across millions of members
Graph Database Nodes and edges representing relationships Modeling co-authorship between authors

Key-Value Stores

Each item is stored as a pair: a unique key and an opaque value. The database doesn't inspect the value's internal structure, it just stores and retrieves it by key.

Key: "member_1042_session"
Value: "eyJhbGciOiJIUzI1NiJ9...token_blob"

This is fast because lookups are direct, there's no query planning or joins involved. Examples include Redis and Amazon DynamoDB.

Document Stores

Data is stored as documents, typically JSON or BSON, where each document can have a different structure. A book record and a member record can both live in the same collection without matching schemas.

{
  "title": "Design Patterns",
  "authors": ["Gamma", "Helm", "Johnson", "Vlissides"],
  "copies_available": 3,
  "reviews": [
    { "member": "Asha", "rating": 5, "comment": "Essential read" }
  ]
}

Notice the reviews array is nested directly inside the book document. There's no separate reviews table and no join needed to fetch a book with its reviews. MongoDB and CouchDB are the well-known examples here.

Column-Family Stores

Data is organized into column families, groups of related columns stored together, rather than complete rows. This is efficient when you frequently query a subset of columns across huge numbers of records.

  • Best for: time-series data, write-heavy workloads, analytics over specific columns
  • Examples: Apache Cassandra, HBase

Graph Databases

Data is stored as nodes (entities) and edges (relationships) rather than tables. Querying relationships, like "find all co-authors of co-authors," is a traversal instead of a chain of joins.

[!TIP]
If your dominant query pattern is "how are these things connected," reach for a graph database before anything else. Relational joins get exponentially slower with relationship depth; graph traversals don't.

MCQ

Which NoSQL type is best suited for storing a book document that includes its reviews nested inside it, without needing a join to retrieve both together?

Why NoSQL?

You've seen the four types. But your library system currently runs fine on a relational database with a few thousand members, so why would you ever migrate? The honest answer is: you might not need to. NoSQL solves specific problems, and it's worth being clear about what those problems actually are before reaching for it.

NoSQL exists to address scenarios where relational databases hit real, measurable limits, not because relational databases are outdated.

Problems That Push Teams Toward NoSQL

  • Schema rigidity: Adding a "digital rental duration" field to books means altering the table and migrating every existing row
  • Vertical scaling ceilings: A single powerful relational server eventually maxes out, and buying a bigger server is expensive and has hard physical limits
  • Join-heavy performance costs: As data grows into millions of rows, multi-table joins get slower, especially across distributed shards
  • Unstructured data: Free-text reviews, sensor logs, or nested JSON from third-party APIs don't map cleanly to fixed columns

When Relational Still Wins

  • Strict consistency requirements: Bank transfers, seat reservations, anything where "almost correct" isn't acceptable
  • Well-defined, stable schema: If your data shape rarely changes, relational's structure is a feature, not a limitation
  • Complex multi-table reporting: SQL's join and aggregation tooling is mature and well-optimized for this
flowchart LR
    A["Growing data + changing schema + scale needs"] --> B{Is strict consistency required?}
    B -->|Yes, e.g. financial transactions| C["Stay Relational SQL"]
    B -->|No, eventual consistency acceptable| D["Consider NoSQL"]

This isn't a one-way decision. Many production systems, including large-scale ones, run both: relational for transactional core data, NoSQL for logs, sessions, or catalogs.

MCQ

A team is building a system that processes bank account transfers where every transaction must be immediately and strictly consistent. Which approach is generally more appropriate?

Advantages of NoSQL

Having weighed when NoSQL makes sense, it's worth breaking down exactly what you gain when you do adopt it for parts of your library system, like the review and session data.

NoSQL databases offer a specific set of structural advantages that stem directly from relaxing the constraints relational databases enforce.

Core Advantages

  • Schema flexibility: A books collection can hold graphic novels with an illustrator field and textbooks with an edition field, side by side, no migration required
  • Horizontal scalability: Instead of upgrading one server, you add more commodity servers, and most NoSQL systems distribute data across them automatically
  • High write and read throughput: Denormalized, document-shaped data often means one read instead of five joined reads
  • Handles large volumes of varied data: Structured, semi-structured, and unstructured data can coexist without forcing a common shape
  • Built for distributed environments: Replication and partitioning are first-class features, not afterthoughts bolted onto a relational engine

A Concrete Comparison

Without denormalization (relational join): fetching a book with its reviews means querying books, then reviews filtered by book_id, then joining the results, three logical steps.

With a document model: fetching a book with its reviews means one lookup, because the reviews already live inside the book document.

[!IMPORTANT]
Flexibility isn't free. Denormalized data means updating a member's name might require changing it in every document that stores it, instead of one row in one table. NoSQL trades write-side complexity for read-side speed.

MCQ

What is the main tradeoff NoSQL databases typically accept in exchange for faster reads through denormalized data?

Comparison of SQL, NoSQL and NewSQL

You now understand SQL's strengths and NoSQL's advantages, but there's a third option that often gets left out of this conversation entirely. What if you want relational structure and strict consistency, but also need to scale horizontally like NoSQL systems do? That gap is exactly what NewSQL was built to close.

NewSQL is a class of database systems that aim to provide the horizontal scalability of NoSQL while retaining the ACID guarantees and relational model of traditional SQL databases.

Side-by-Side Comparison

Feature SQL NoSQL NewSQL
Data model Fixed tables, rows, columns Key-value, document, column, graph Relational, tables
Schema Rigid, predefined Flexible or schema-less Rigid, predefined
Consistency Strong (ACID) Often eventual (BASE) Strong (ACID)
Scaling Primarily vertical Horizontal Horizontal
Query language SQL Varies (Mongo query, CQL, Gremlin) SQL
Example systems PostgreSQL, MySQL MongoDB, Cassandra, Redis Google Spanner, CockroachDB
Best for Structured, transactional data Massive, varied, fast-growing data Structured data needing global scale

Where NewSQL Fits for the Library System

If your library platform expanded into a multi-country chain needing strict consistency on loan records across every branch simultaneously, plain relational wouldn't scale horizontally well, and plain NoSQL might risk eventual consistency issues on due-date enforcement. NewSQL systems like CockroachDB target exactly this middle ground.

[!NOTE]
NewSQL is not "NoSQL that got faster." It's architecturally closer to SQL, relational tables and ACID transactions, but re-engineered internally to distribute across nodes the way NoSQL systems do.

MCQ

Which characteristic best distinguishes NewSQL from both traditional SQL and NoSQL?

Aggregates

You've compared the three database families at a high level. Now it's time to go inside NoSQL's data modeling itself, and the first concept to understand is the aggregate, because it explains why document and key-value stores are structured the way they are.

An aggregate is a collection of related objects that are treated as a single unit for data manipulation and consistency purposes, typically everything you'd need to fetch or update together in one operation.

Why Aggregates Exist

  • Without aggregates: fetching a book and its reviews means separate queries against separate tables joined at read time
  • With aggregates: the book and its reviews are grouped into one aggregate, so one fetch returns everything

This grouping decision is a modeling choice, and it directly shapes performance, since operations on a single aggregate are typically atomic and fast, while operations spanning multiple aggregates are not.

Aggregate Boundaries in the Library Example

{
  "book_id": "B-1042",
  "title": "Clean Code",
  "author": "Robert Martin",
  "copies_available": 2,
  "reviews": [
    { "member_id": "M-88", "rating": 4 },
    { "member_id": "M-91", "rating": 5 }
  ]
}

Here, the book and its reviews form one aggregate, they're stored, fetched, and updated together. But member_id in each review is just a reference, not a nested member document. Members are a separate aggregate because they're updated independently and used across many books, not just this one.

  • Group into one aggregate when: data is almost always read or written together, and one item doesn't outgrow the other unboundedly
  • Keep as separate aggregates when: data is shared across many parents, updated independently, or could grow unbounded (a popular book could have thousands of reviews)

[!WARNING]
Nesting unbounded data, like every review a member has ever written, inside a single document can cause that document to grow indefinitely, hurting performance and sometimes exceeding document size limits.

MCQ

Why would member profile data typically be modeled as a separate aggregate from book reviews rather than nested inside each review?

Key-value and Document Data Models

With aggregates as the underlying concept, you can now look at the two data models that use them most directly: key-value and document stores. Both group related data together, but they differ in how much the database itself understands about what's inside that grouping.

Key-value and document data models both store data as self-contained units addressed by a key, but they differ sharply in whether the database can see inside the value.

Key-Value Model: The Opaque Box

In a key-value store, the value is a black box to the database. It could be a string, a serialized object, or binary blob, the database doesn't parse or query its internal fields.

GET member_1042_session
→ "eyJhbGciOiJIUzI1NiJ9...token_blob"

The database can retrieve this instantly by key, but it cannot run a query like "find all sessions expiring in the next hour" without reading and inspecting every value manually.

  • Best for: caching, session storage, shopping carts, feature flags
  • Cannot do well: querying by internal fields, filtering, or partial updates

Document Model: The Transparent Box

A document store also addresses data by key, but the value, the document, has a known structure (usually JSON) that the database can read, index, and query into.

{
  "_id": "B-1042",
  "title": "Clean Code",
  "genre": "Software Engineering",
  "copies_available": 2
}

Because the database understands this structure, you can query directly on fields inside it, for instance fetching every book where genre equals "Software Engineering" without touching unrelated books.

Comparing the Two Directly

Feature Key-Value Document
Value visibility Opaque to the database Structured and queryable
Query capability By key only By key or by internal fields
Typical use Caching, sessions Catalogs, content, profiles
Example systems Redis, DynamoDB MongoDB, CouchDB

[!TIP]
If you find yourself wanting to filter, sort, or search inside your "values," you've outgrown a key-value store and need a document store instead.

MCQ

What is the fundamental difference in how a key-value store and a document store treat the data they hold?

Graph Databases

Key-value and document models both group data around a single entity, a book, a member. But your library system also has a use case those models handle badly: "which authors have co-authored a book with someone who has also co-authored with Robert Martin?" That's a relationship-heavy question, and it's exactly what graph databases are built for.

A graph database stores data as nodes representing entities and edges representing the relationships between them, making relationship traversal a direct operation instead of a chain of joins.

Structure: Nodes, Edges, and Properties

  • Nodes: entities like Author, Book, Member
  • Edges: relationships like CO_AUTHORED, BORROWED, REVIEWED, often directional and labeled
  • Properties: key-value attributes attached to either nodes or edges, like a rating property on a REVIEWED edge
graph LR
    A["Author: Robert Martin"] -- CO_AUTHORED --> B["Author: Micah Martin"]
    A -- WROTE --> C["Book: Clean Code"]
    D["Member: Asha"] -- BORROWED --> C
    D -- REVIEWED --> C

This graph shows how relationships fan out naturally. Finding everyone connected to Robert Martin within two hops is a traversal starting at that node, not a multi-table join.

Why Traversal Beats Joins Here

Without a graph database: finding second-degree co-authors means joining the authors table to itself twice through a co_authorship table, and performance degrades further with every additional hop.

With a graph database: the same query walks edges directly from the starting node, and performance stays roughly proportional to the size of the relevant subgraph, not the entire dataset.

When to Reach for Graph Databases

  • Social networks and friend recommendation systems
  • Fraud detection through transaction relationship patterns
  • Recommendation engines ("members who borrowed this also borrowed...")
  • Knowledge graphs and dependency mapping

[!NOTE]
Graph databases like Neo4j are not typically used as your primary database for high-volume transactional data. They excel specifically at relationship-heavy queries, and teams often run them alongside a document or relational store.

MCQ

Why does querying "friends of friends" style relationships tend to perform better in a graph database than in a relational database as the relationship depth increases?

Map-Reduce

You've now seen how data is modeled and connected. But once your library system has millions of loan records, a new question appears: how do you compute something across all of them, like "total books borrowed per genre last year," without a single server choking on the workload? That's the problem map-reduce was designed to solve.

Map-Reduce is a programming model for processing large datasets by splitting the work into two phases: a map phase that processes data in parallel across many machines, and a reduce phase that aggregates those results into a final output.

The Two Phases

Step 1: Map Phase

Each machine processes a chunk of the data independently and emits intermediate key-value pairs.

Input: loan records
Map function: for each loan, emit (genre, 1)

("Fiction", 1)
("Science Fiction", 1)
("Fiction", 1)

Every machine does this in parallel on its own slice of data, with no communication needed between machines during this phase.

Step 2: Reduce Phase

Intermediate results with the same key are grouped together and combined into a final result.

Reduce function: sum all values per genre

("Fiction", 2)
("Science Fiction", 1)

The reduce phase takes the scattered intermediate output and collapses it into the answer you actually wanted, total loans per genre.

Why This Matters at Scale

  • Without map-reduce: counting loans by genre across 50 million records on one machine means one long sequential scan
  • With map-reduce: the same 50 million records are split across 100 machines, each processing 500,000 records in parallel, then combined
flowchart LR
    A["Loan Records"] --> B1["Map: Machine 1"]
    A --> B2["Map: Machine 2"]
    A --> B3["Map: Machine 3"]
    B1 --> C["Reduce: Combine by Genre"]
    B2 --> C
    B3 --> C
    C --> D["Final Aggregated Report"]

[!TIP]
Map-reduce is well suited for batch analytics, like nightly reports, but poorly suited for real-time queries, since it processes data in bulk passes rather than responding to individual requests instantly.

MCQ

In the map-reduce model, what happens during the map phase?

Partitioning and Combining

Map-reduce relies on splitting data across machines, but that raises the underlying question every distributed NoSQL system has to answer: how do you decide which machine holds which piece of data in the first place, and how do results come back together correctly? That's partitioning and combining.

Partitioning is the process of splitting a dataset across multiple servers (nodes) so each node holds only a portion of the total data, enabling horizontal scalability.

How Partitioning Works

A partition key determines which node a given piece of data lives on. For the library system, you might partition loan records by member_id, so all of one member's loans live on the same node.

member_id "M-1001" → hashed → Node 3
member_id "M-1002" → hashed → Node 1
member_id "M-1042" → hashed → Node 3

Every write and read for a given member is now routed to a predictable node instead of being scattered randomly, which keeps lookups fast.

Partitioning Strategies

Strategy How It Works Tradeoff
Hash partitioning Key is hashed to determine node Even distribution, but range queries are harder
Range partitioning Data split by key ranges (A-M, N-Z) Fast range queries, but risk of uneven "hot" partitions
Directory-based A lookup service maps keys to nodes Flexible, but the lookup service becomes a bottleneck if not managed well

[!WARNING]
A poorly chosen partition key creates "hot partitions," where one node handles disproportionately more traffic than others, undermining the entire point of partitioning.

Combining Results Across Partitions

Once data is partitioned, some queries need results from every partition. This is where combining comes in, gathering partial results from each node and merging them into one answer, conceptually similar to the reduce phase in map-reduce.

Without combining: asking "how many total books are currently on loan" would only tell you the count from whichever single node you happened to query.

With combining: the query fans out to every partition, each returns its local count, and those counts are summed into the true total.

flowchart LR
    Q["Query: Total Books on Loan"] --> N1["Node 1: 340"]
    Q --> N2["Node 2: 275"]
    Q --> N3["Node 3: 410"]
    N1 --> C["Combine: Sum = 1025"]
    N2 --> C
    N3 --> C

MCQ

What is a "hot partition" and why is it a problem?