Background
Sections
IntroductionRequirements & Problem AnalysisConstraints & AssumptionsEstimation TechniquesFunctional vs Non-Functional RequirementsMoSCoW PrioritizationSystem Design FundamentalsArchitecture DiagramClass DiagramComponent DiagramData Flow Diagram (DFD)ER Diagram (Entity-Relationship Diagram)High Level Design (HLD)Low Level Design (LLD)Sequence DiagramState DiagramUse Case DiagramData StorageDocument StoresFile StorageGraph DatabasesIn-Memory DatabasesKey-Value StoresNewSQLNoSQL DatabasesObject StorageSQL Databases (RDBMS)Time-Series DatabasesWide-Column StoresDatabase ConceptsACID PropertiesCAP TheoremConsistency ModelsIndexingNormalization & DenormalizationReplicationSharding & PartitioningTransactions & Isolation LevelsScalabilityAuto-Scaling & ElasticityConsensus & Leader ElectionLoad BalancingReplication & Read ReplicasSharding & PartitioningVertical vs Horizontal ScalingAvailability & ReliabilityBackup & Data DurabilityCircuit BreakerData ConsistencyDisaster RecoveryFault Tolerance & FailoverGraceful DegradationHigh AvailabilityNetworkingCDNDNSFirewalls & VPNHTTP & HTTPSLoad Balancer & Reverse ProxyTCP/IP & UDPWebSocketsCachingCache InvalidationCache Read/Write PatternsCaching LayersEviction PoliciesRedis vs MemcachedMessaging & CommunicationDead-Letter QueueIdempotencyKafka vs RabbitMQ vs SQSMessage QueuesPub/SubCompute & ServicesAPI GatewayContainers & OrchestrationMonolith vs MicroservicesServerlessService DiscoveryService MeshWeb Server & App ServerAPI DesignAPI Versioning & IdempotencyAuthentication & AuthorizationGraphQLgRPCPaginationRate Limiting & ThrottlingRESTSecurityAuthentication & AuthorizationData PrivacyEncryptionInput Validation & InjectionOAuth2 & JWTSecrets ManagementXSS & CSRFStorage & File SystemsBackup & RetentionBlock vs File vs Object StorageData Lakes & WarehousesDistributed File SystemsEphemeral StorageObservability & MonitoringDistributed TracingHealth ChecksLoggingMetricsSLI, SLO, SLADesign PatternsBulkhead PatternCircuit Breaker PatternCreational PatternsRate Limiter PatternRetry PatternStructural & Behavioral Patterns

NoSQL Databases

7 min read

In a Nutshell

NoSQL is not one thing — it's an umbrella term for databases that trade parts of the SQL contract (rigid schemas, joins, full ACID) for scale, flexibility, or specialized performance. The name is misleading; a better name would be "Not Only SQL." NoSQL databases exist because relational databases, despite being excellent, have real limitations at extreme scale: a single primary bottleneck for writes, expensive cross-shard joins, and rigid schemas that make frequent changes painful. NoSQL solves specific problems — but it creates new ones.

2D minimalistic diagram showing SQL as a single structured box on the left, with four arrows branching out to the right to four different NoSQL shapes: key-value (simple pairs), document (nested JSON), wide-column (sparse grid), and graph (nodes and edges)

How It Actually Works

The Four NoSQL Families

Family Data Model Access Pattern Examples
Key-Value Key → opaque value Get/set by exact key Redis, DynamoDB, Riak
Document Key → structured JSON/BSON document Query by any field in the document MongoDB, CouchDB, Firestore
Wide-Column Row key → sparse, dynamic columns Range scans on row key, high write throughput Cassandra, HBase, ScyllaDB
Graph Nodes + edges with properties Traverse relationships Neo4j, Amazon Neptune, JanusGraph

What They All Share

Despite their differences, all NoSQL databases share some common traits:

  1. Schema flexibility — No predefined schema (or a very flexible one). Fields can be added without migrations.
  2. Horizontal scaling — Designed to shard data across many machines from the start.
  3. Denormalized data — Data is typically stored in the shape it will be queried, not normalized.
  4. Weaker consistency — Most offer tunable consistency rather than strict ACID (eventual consistency is the default).
  5. No joins — Relationships between records must be handled in application code or by denormalization.

SQL vs NoSQL Decision Framework

Question If Yes → SQL If Yes → NoSQL
Does data have many relationships?
Do you need joins in queries?
Is ACID required (money, inventory)?
Will query patterns change frequently?
Is write throughput the bottleneck?
Is each record self-contained?
Do you need horizontal scaling from day one?
Is schema evolving rapidly (prototyping)?
Is the access pattern very specific (key lookup, graph traversal)?

The Trade-Offs You Accept

When you choose NoSQL, you're accepting specific trade-offs:

What You Gain What You Lose
Horizontal write scaling Cross-record transactions
Schema flexibility Data integrity enforcement
Optimized access patterns Ad-hoc query flexibility
High throughput for specific operations Joins (must denormalize or do in app code)
Simpler read path (data shaped for query) Harder write path (must maintain denormalized copies)

The fundamental shift: in SQL, you model the data and let the query language handle access. In NoSQL, you model the query and shape the data to serve it.

2D minimalistic diagram showing two approaches side by side: left side 'SQL Approach' shows normalized tables with flexible queries fanning out, right side 'NoSQL Approach' shows denormalized data shaped exactly like the query it serves

Common Mistakes

  1. Using NoSQL because it's "modern" — If your data has relationships and you need transactions, SQL is the right choice. NoSQL for a banking system is a mistake.
  2. Treating all NoSQL as the same — Key-value and graph databases solve completely different problems. "We'll use NoSQL" is not a design decision.
  3. Ignoring the join problem — Without joins, you either denormalize (write amplification) or join in application code (latency). Neither is free.
  4. Assuming NoSQL = no schema — The schema moves to application code, which is worse because it's not enforced. Schema-on-read means bugs are discovered at read time instead of write time.

Seeing It in Action

Scenario: Choosing the right NoSQL family for different use cases

Use Case Best NoSQL Family Why
Session storage for a web app Key-Value (Redis) Simple get/set by session ID, TTL expiry, microsecond access
User profiles for a social platform Document (MongoDB) Each profile is self-contained, schema varies by user type, no joins needed
IoT sensor readings (billions/day) Wide-Column (Cassandra) Massive write throughput, time-range queries, data partitioned by device ID
Social network "friends of friends" Graph (Neo4j) Traversal queries (3 hops deep) that would require 6 self-joins in SQL
Shopping cart Document or Key-Value Cart is self-contained, temporary, per-user — no relationships to other entities
Financial transactions ledger SQL ❌ Not NoSQL ACID transactions, double-entry consistency — NoSQL is wrong here

Interview Questions

  1. Q: When would you choose NoSQL over SQL for a new project? Give specific criteria. Hint: When records are self-contained (no joins needed), write throughput exceeds what a single SQL primary handles, schema is evolving rapidly, or the access pattern is very specific (key lookup, time-range scans, graph traversal). Never choose NoSQL just because "it's more scalable" — that's a myth at most scales.

  2. Q: Explain the difference between schema-on-write (SQL) and schema-on-read (NoSQL). What are the implications? Hint: Schema-on-write: the database enforces structure at write time — invalid data is rejected. Schema-on-read: anything can be written, and the application interprets structure at read time — invalid data is discovered later. Schema-on-write catches errors earlier but makes changes harder. Schema-on-read is flexible but pushes validation into application code.

  3. Q: You're designing a system that needs both strong consistency (payments) and high write throughput (activity logs). How do you handle this? Hint: Polyglot persistence — use SQL (PostgreSQL) for payments where ACID is required, and NoSQL (Cassandra or DynamoDB) for activity logs where eventual consistency is fine and write volume is high. This is the standard pattern: different databases for different access patterns within the same system.

  4. Q: What does "model the query, not the data" mean in NoSQL? Give an example. Hint: In SQL, you normalize data and write any query you want. In NoSQL (e.g., Cassandra), you design each table around a specific query. If you need "get orders by user" AND "get orders by date," you create two tables with the same data partitioned differently. Write amplification is the cost of read optimization.

  5. Q: What problems does denormalization create in a NoSQL database, and how do you handle them? Hint: Data can become inconsistent across copies (user changes name — do all denormalized copies update?). Solutions: accept eventual consistency, use change data capture (CDC) to propagate updates, or batch reconciliation jobs. The key trade-off: faster reads at the cost of more complex writes.

References

Dive Deeper