Explore the world beyond rigid tables: when to use NoSQL for large scale and dynamic schemas, the four data models — document, key-value, wide-column, and in-memory cache — and a comparison of DynamoDB, ElastiCache, Firestore, Bigtable, Memorystore, Cosmos DB, and Azure Cache for Redis, complete with JSON item examples and CLI commands.

In episode 9 we discussed relational databases: managed RDBMS, multi-AZ high availability, and distributed SQL like Aurora and Spanner. Relational excels because of strict schemas, ACID transactions, and a universal query language. But more and more modern applications hold data that's uncomfortable being forced into tables with fixed columns — data whose shape keeps changing, that must be read with very low latency, or that has to serve millions of requests per second.
Episode 10 covers the opposite: NoSQL and in-memory databases. You'll learn when relational starts to be insufficient, the four NoSQL data models, the role of in-memory caches, the managed services on AWS, GCP, and Azure, and the practice of writing JSON items and storing them in DynamoDB via the CLI.
There's no bad database — there are only misplaced databases. Relational is great for structured, related data that needs strict consistency, like financial transactions. It struggles in three conditions:
| Aspect | Relational | NoSQL |
|---|---|---|
| Schema | Rigid, defined before data arrives | Flexible, each item may differ |
| Consistency | ACID, strong consistency | Generally eventual consistency |
| Scale | Vertical (bigger instance) or replication | Horizontal (distribution across many nodes) |
| Query | SQL, joins between tables | Key-based or access-pattern based |
Tip
Think of a library with two systems. Relational is a bookcase with uniform columns — every book must fill the same columns. NoSQL is a warehouse of envelopes: each envelope can hold anything in its own format, and the clerk knows exactly which shelf it's stored on based on its label. For a tidy collection, a bookcase is better; for a varied, constantly changing catalog, the envelope warehouse is far more practical.
The word "NoSQL" unites four very different families. Treating them all the same is a beginner's mistake — your choice must depend on your application's access patterns.
Data is stored as documents in JSON or BSON format, like a dictionary entry that can nest. One document can hold a name, address, and order list all at once — no joins needed. Suitable for user profiles, product catalogs, and CMS content. Representatives: MongoDB, Firestore, Cosmos DB.
The simplest model: one unique key mapped to one value, like a giant dictionary. Reads always go through the key — very fast and very scalable, but with no complex query capability. Suitable for user sessions, feature flags, and small reference data. Representatives: DynamoDB in simple mode, Redis, Memcached.
Data is stored per row, but each row may have different columns — a hybrid of tables and documents. Suitable for large analytics and time-series data, where one row holds millions of timestamped values. Representatives: Cassandra, Bigtable.
Data is stored entirely in RAM, not on disk. The result: reads in microseconds to milliseconds. This family is fundamentally different — its purpose isn't storing primary data, but accelerating frequently-read data. Representatives: Redis, Memcached.
Note
A useful rule of thumb: document for data whose shape changes, key-value for super-fast key-based lookups, wide-column for large-scale analytics, and in-memory for acceleration. Don't pick a product first — pick your data pattern and access pattern, then the matching service name.
An in-memory cache exists for one simple reason: many database queries repeatedly answer the same question. Every time an application reads the same profile a hundred times per second, the database repeats work whose results are identical.
The classic pattern is called cache-aside: the application checks the cache first; if it's there, use it directly (cache hit); if not, fetch from the database, store it in the cache, then return it (cache miss). Each item gets a TTL (time to live) — an expiration — so data doesn't go stale forever.
| Aspect | Without cache | With cache |
|---|---|---|
| Read latency | 10-50 ms (database) | Below 1 ms (RAM) |
| Database load | Thousands of queries per second | Minimal, only on cache miss |
| Cost | Expensive (large database instance) | Cheap (small cluster in front of the DB) |
Imagine a coffee shop that's often asked "what's on the menu today?" — answering that question from the menu board (cache) is far faster than opening the fridge every time (database). That's the essence of caching.
All three major clouds provide every family above as a managed service — you install nothing, just create resources and call their APIs:
Important
A hallmark of managed products is provisioned or on-demand capacity: you decide how much read-write capacity to provision, usually counted in units like RCU/WCU in DynamoDB. Setting this too low triggers throttling — requests get rejected with status 429 when capacity runs out. To start learning, use on-demand mode to pay only for actual usage, then study provisioned capacity once you understand your traffic pattern.
DynamoDB stores data as items, and each item is a collection of attributes. Its strength lies in the flexible schema: the following item has different attributes within the same table — something impossible in a relational table.
{
"pk": { "S": "user#42" },
"sk": { "S": "profile" },
"name": { "S": "Arman" },
"age": { "N": "29" },
"active": { "BOOL": true },
"skills": { "SS": ["cloud", "devops"] },
"address": {
"M": {
"city": { "S": "Jakarta" },
"zip": { "S": "12345" }
}
}
}Each attribute is written with its data type: S for string, N for number, BOOL for boolean, SS for set of strings, and M for a nested map. Another item in the same table could contain just two attributes — there are no schema constraints at the table level.
Storing this item in the users table is done with aws dynamodb put-item:
aws dynamodb put-item \
--table-name users \
--item '{
"pk": { "S": "user#42" },
"sk": { "S": "profile" },
"name": { "S": "Arman" },
"age": { "N": "29" },
"skills": { "SS": ["cloud", "devops"] }
}'Notice the recommended DynamoDB pattern: the pk and sk pair are the partition key and sort key — this is the item's "address" in the table. A good key design mirrors how the application accesses data: if the application always reads profiles by user ID, then pk holds that ID.
Caution
An important DynamoDB rule: you cannot freely scan large data to search — you must always query by key. DynamoDB table design starts from the question "what queries will the application send?", not from the data's structure. That's why key design and single-table design are core skills for a NoSQL engineer.
| Need | AWS | GCP | Azure |
|---|---|---|---|
| Managed NoSQL | DynamoDB | Firestore, Bigtable | Cosmos DB |
| In-memory cache | ElastiCache | Memorystore | Azure Cache for Redis |
The concepts behind all these products are identical: flexible schemas for NoSQL, and storage in RAM for caches. Moving between providers means learning new APIs and names, not relearning concepts. The most valuable skill in the job market is the ability to judge when to use document, key-value, wide-column, or cache — not memorizing buttons in one console.
In this episode 10 you understood when NoSQL is needed — large scale, dynamic schemas, and extreme read patterns — along with its four data models: document, key-value, wide-column, and in-memory. You also got to know the managed services on the three clouds — DynamoDB, Firestore, Bigtable, Cosmos DB, ElastiCache, Memorystore, and Azure Cache for Redis — plus the practice of writing JSON items and storing them via aws dynamodb put-item.
The keys to take away:
Everything we've discussed so far still requires you to think about servers — how much capacity, how many instances, how many nodes. Episode 11 will flip that logic entirely: Serverless & Function as a Service (FaaS) — applications that run only when an event occurs, scale from zero to millions, and bill per execution in milliseconds.