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

Key-Value Stores

6 min read

In a Nutshell

A key-value store is the simplest database model: you give it a key (a string), and it returns a value (a blob, string, number, or data structure). That's it — no schema, no columns, no joins, no query language beyond "get this key" and "set this key." This simplicity is the point: it enables O(1) lookup and enormous throughput. Key-value stores are the backbone of caching layers, session storage, rate limiters, and counters — anywhere you need fast, simple access by a known identifier.

2D minimalistic diagram showing a key-value store as a simple two-column table: left column has keys like 'user:123', 'session:abc', right column has values like JSON blobs and counters, with a GET arrow going in and a value arrow coming out

How It Actually Works

Core Operations

Operation Complexity Example
GET key O(1) GET user:123{"name": "Alice", "email": "..."}
SET key value O(1) SET session:abc {user_id: 123, expires: ...}
DELETE key O(1) DELETE session:abc
TTL key seconds O(1) EXPIRE session:abc 3600 (auto-delete after 1 hour)
INCR key O(1) INCR page_views:home (atomic counter)

Redis vs DynamoDB vs Memcached

Aspect Redis DynamoDB Memcached
Storage In-memory (with optional persistence) On-disk (SSD) with DAX cache In-memory only
Data structures Strings, hashes, lists, sets, sorted sets, streams Items with attributes (more like document-KV hybrid) Simple strings only
Persistence RDB snapshots + AOF log Fully durable by default None — pure cache
Scaling Cluster mode (hash slots) Automatic partitioning, on-demand scaling Client-side sharding
Best for Caching, sessions, leaderboards, pub/sub, rate limiting Serverless apps, any-scale key-value with durability Pure caching (simple, fast)
Latency Sub-millisecond Single-digit millisecond Sub-millisecond

Use Cases

Use Case Key Pattern Value Why Key-Value
Session storage session:{session_id} User session JSON Fast lookup by session ID, TTL for expiry
Caching cache:user:{user_id} Cached user profile Avoid database reads for hot data
Rate limiting rate:{api_key}:{minute} Counter Atomic increment, TTL resets per window
Distributed locks lock:{resource_id} Lock holder ID SET with NX (only if not exists) + TTL
Leaderboard leaderboard:game1 Sorted set of scores Redis sorted sets give O(log n) rank queries
Feature flags flag:{feature_name} Boolean/percentage Fast lookup, instant updates without deployment

Key Design Patterns

Namespace your keys: Use colons to create a hierarchy: user:123:profile, user:123:sessions, order:456:status. This prevents collisions and makes keys self-documenting.

Use TTL everywhere: Every cached value should have a TTL. Without it, your cache grows unbounded and eventually exhausts memory.

Atomic operations matter: INCR, SETNX (set if not exists), and GETSET are atomic — they prevent race conditions without external locking.

2D minimalistic diagram showing Redis data structures: a string box, a hash box with field-value pairs, a list box with ordered items, a set box with unique items, and a sorted set box with score-value pairs

Seeing It in Action

Scenario: Rate limiter using Redis

import redis
import time

r = redis.Redis()

def is_rate_limited(api_key, max_requests=100, window_seconds=60):
    """Sliding window rate limiter using Redis."""
    key = f"rate:{api_key}:{int(time.time()) // window_seconds}"

    # Atomic increment + set TTL on first use
    current = r.incr(key)
    if current == 1:
        r.expire(key, window_seconds)

    return current > max_requests

# Usage
if is_rate_limited("client_abc"):
    return HttpResponse(status=429, headers={"Retry-After": "60"})

Why Redis is right here:

  • O(1) operationsINCR is atomic and sub-millisecond
  • TTL — Keys auto-expire, no cleanup needed
  • Throughput — Redis handles 100K+ operations/second on a single instance
  • No complex queries — just increment a counter by key

Interview Questions

  1. Q: When would you choose Redis over DynamoDB, and vice versa? Hint: Redis: when you need sub-millisecond latency, rich data structures (sorted sets, pub/sub), and the working set fits in memory. DynamoDB: when you need durable key-value storage, automatic scaling, and serverless pricing. Redis is a cache/accelerator; DynamoDB is a primary database.

  2. Q: How would you implement a distributed lock using Redis? Hint: SET lock:{resource} {owner_id} NX EX 30 — sets the key only if it doesn't exist (NX), with a 30-second TTL (EX). To release: verify you're the owner, then DELETE. Pitfall: if the process holding the lock dies, the TTL ensures the lock is eventually released. For stronger guarantees, use RedLock (consensus across multiple Redis instances).

  3. Q: Your Redis instance is running out of memory. What do you do? Hint: 1) Set TTLs on all keys (many apps forget this). 2) Review eviction policy (allkeys-lru is usually best for caches). 3) Check for hot keys consuming disproportionate memory. 4) Scale to Redis Cluster (horizontal sharding by hash slot). 5) Consider tiered caching — Redis for hot data, DynamoDB or disk for warm data.

  4. Q: How does Redis persistence work, and when would you enable it? Hint: Two mechanisms: RDB (periodic snapshots — fast recovery, some data loss) and AOF (append-only log — minimal data loss, slower recovery). Enable persistence when Redis is used as a primary data store (not just a cache). For pure caching, persistence is unnecessary — a miss just means a database read.

  5. Q: Design a leaderboard that shows the top 10 players and any player's rank in O(log n) time. Hint: Redis sorted set: ZADD leaderboard {score} {player_id}. Top 10: ZREVRANGE leaderboard 0 9 WITHSCORES. Player rank: ZREVRANK leaderboard {player_id}. All O(log n). This is far more efficient than a SQL ORDER BY score DESC LIMIT 10 on a large table.

References

Dive Deeper

  • Redis University — free courses on Redis data structures and patterns
  • Redis in Action by Josiah Carlson — practical Redis patterns for real applications
  • DynamoDB Single Table Design — Alex DeBrie's guide to advanced DynamoDB modeling