Introduction

Memcached is a high-performance, distributed memory caching system that stores data in RAM for fast access. It’s designed to speed up dynamic web applications by reducing database load and improving response times. Understanding Memcached is essential for system design interviews and building high-performance applications.

This guide covers:

  • Memcached Fundamentals: Architecture, key-value storage, and operations
  • Distributed Caching: Multi-server setup and consistent hashing
  • Cache Strategies: Cache-aside, write-through, write-back
  • Performance Optimization: Connection pooling, pipelining, and monitoring
  • Best Practices: Cache invalidation, key design, and error handling

What is Memcached?

Memcached is a distributed memory caching system that:

  • In-Memory Storage: Stores data in RAM for fast access
  • Key-Value Store: Simple key-value data model
  • Distributed: Multiple servers work together
  • High Performance: Sub-millisecond latency
  • Simple API: Easy to integrate

Key Concepts

Key: Unique identifier for cached data

Value: Data stored in cache

Expiration: Time-to-live (TTL) for cached items

Server: Memcached instance

Client: Application that uses Memcached

Cluster: Group of Memcached servers

Architecture

High-Level Architecture

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Client    │────▶│   Client    │────▶│   Client    │
│ Application │     │ Application │     │ Application │
└──────┬──────┘     └──────┬──────┘     └──────┬──────┘
       │                    │                    │
       └────────────────────┴────────────────────┘
                            │
                            │ Memcached Protocol
                            │
                            ▼
              ┌─────────────────────────┐
              │   Memcached Client      │
              │   (Hash Distribution)   │
              └──────┬──────────────────┘
                     │
       ┌─────────────┴─────────────┐
       │                           │
┌──────▼──────┐           ┌───────▼──────┐
│  Memcached  │           │  Memcached   │
│  Server 1   │           │  Server 2    │
│  (RAM)      │           │  (RAM)       │
└─────────────┘           └─────────────┘

Explanation:

  • Client Applications: Applications that use Memcached for caching (e.g., web applications, APIs, databases).
  • Memcached Client: Client library that distributes keys across Memcached servers using consistent hashing.
  • Memcached Servers: In-memory key-value stores that cache data in RAM for fast access. Multiple servers form a distributed cache cluster.

Core Architecture

┌─────────────────────────────────────────────────────────┐
│              Application                                 │
│                                                          │
│  ┌──────────────────────────────────────────────────┐   │
│  │         Memcached Client                        │   │
│  │  (Connection Pooling, Hashing)                  │   │
│  └──────────────────────────────────────────────────┘   │
│                          │                               │
│        ┌─────────────────┴─────────────────┐          │
│        │                                     │          │
│  ┌─────▼──────┐                    ┌───────▼──────┐   │
│  │ Memcached  │                    │  Memcached    │   │
│  │  Server 1  │                    │   Server 2     │   │
│  │  (RAM)     │                    │    (RAM)       │   │
│  └────────────┘                    └────────────────┘   │
└──────────────────────────────────────────────────────────┘

Data Model

Key-Value Storage

Key:

  • String identifier
  • Maximum 250 bytes
  • Unique within cluster

Value:

  • Binary data
  • Maximum 1MB per item
  • Any serializable data

Example:

Key: "user:123"
Value: {"id": 123, "name": "John", "email": "john@example.com"}
TTL: 3600 seconds

Operations

SET:

# Store value
memcache_client.set('user:123', user_data, time=3600)

GET:

# Retrieve value
user_data = memcache_client.get('user:123')

DELETE:

# Delete value
memcache_client.delete('user:123')

INCREMENT/DECREMENT:

# Increment counter
memcache_client.incr('counter:views', 1)

# Decrement counter
memcache_client.decr('counter:views', 1)

Distributed Caching

Consistent Hashing

How it Works:

  • Keys mapped to servers using hash function
  • Server addition/removal affects minimal keys
  • Even distribution

Example:

# Hash key to server
def get_server(key, servers):
    hash_value = hash(key)
    server_index = hash_value % len(servers)
    return servers[server_index]

# Key "user:123" → Server 1
# Key "user:456" → Server 2

Benefits:

  • Even distribution
  • Minimal rehashing on server changes
  • Scalability

Multi-Server Setup

Configuration:

# Connect to multiple servers
servers = [
    'memcached1.example.com:11211',
    'memcached2.example.com:11211',
    'memcached3.example.com:11211'
]

client = memcache.Client(servers)

Benefits:

  • Horizontal scaling
  • High availability
  • Load distribution

Cache Strategies

1. Cache-Aside (Lazy Loading)

Pattern:

1. Check cache
2. If miss, read from database
3. Store in cache
4. Return data

Example:

def get_user(user_id):
    # Check cache
    cache_key = f'user:{user_id}'
    user = memcache_client.get(cache_key)
    
    if user is None:
        # Cache miss - read from database
        user = database.get_user(user_id)
        # Store in cache
        memcache_client.set(cache_key, user, time=3600)
    
    return user

Pros:

  • Simple to implement
  • Cache only what’s accessed
  • Flexible

Cons:

  • Cache miss penalty
  • Potential stale data
  • Two round trips on miss

2. Write-Through

Pattern:

1. Write to cache
2. Write to database
3. Return success

Example:

def update_user(user_id, user_data):
    # Write to cache
    cache_key = f'user:{user_id}'
    memcache_client.set(cache_key, user_data, time=3600)
    
    # Write to database
    database.update_user(user_id, user_data)

Pros:

  • Cache always up-to-date
  • No stale data
  • Simple

Cons:

  • Write latency
  • Cache may not be needed
  • More writes

3. Write-Back (Write-Behind)

Pattern:

1. Write to cache only
2. Return success
3. Async write to database

Example:

def update_user(user_id, user_data):
    # Write to cache
    cache_key = f'user:{user_id}'
    memcache_client.set(cache_key, user_data, time=3600)
    
    # Async write to database
    async_queue.enqueue(database.update_user, user_id, user_data)

Pros:

  • Fast writes
  • Reduced database load
  • Better performance

Cons:

  • Data loss risk
  • Complexity
  • Eventual consistency

Cache Invalidation

TTL-Based Expiration

Automatic Expiration:

# Set with TTL
memcache_client.set('user:123', user_data, time=3600)  # 1 hour

Benefits:

  • Automatic cleanup
  • Simple
  • No manual invalidation

Manual Invalidation

Delete on Update:

def update_user(user_id, user_data):
    # Update database
    database.update_user(user_id, user_data)
    
    # Invalidate cache
    cache_key = f'user:{user_id}'
    memcache_client.delete(cache_key)

Update on Write:

def update_user(user_id, user_data):
    # Update database
    database.update_user(user_id, user_data)
    
    # Update cache
    cache_key = f'user:{user_id}'
    memcache_client.set(cache_key, user_data, time=3600)

Cache Warming

Pre-populate Cache:

def warm_cache():
    # Load frequently accessed data
    popular_users = database.get_popular_users()
    
    for user in popular_users:
        cache_key = f'user:{user.id}'
        memcache_client.set(cache_key, user, time=3600)

Performance Optimization

Connection Pooling

Reuse Connections:

# Create connection pool
pool = memcache.Client(
    ['memcached1:11211', 'memcached2:11211'],
    server_max_value_length=1024*1024
)

Benefits:

  • Reduced overhead
  • Better performance
  • Resource efficiency

Pipelining

Batch Operations:

# Multiple operations in one request
results = memcache_client.get_multi(['key1', 'key2', 'key3'])

Benefits:

  • Reduced network overhead
  • Better throughput
  • Efficiency

Key Design

Good Keys:

# Descriptive and unique
'user:123'
'product:456:details'
'session:abc123'

Bad Keys:

# Too generic
'data'
'item'
'info'

Memory Management

LRU Eviction:

  • Least recently used items evicted
  • Automatic when memory full
  • No manual cleanup needed

Memory Allocation:

# Start with memory limit
memcached -m 1024  # 1GB memory

Monitoring

Statistics

Get Stats:

stats = memcache_client.get_stats()
print(stats)

Key Metrics:

  • hits: Cache hits
  • misses: Cache misses
  • evictions: Items evicted
  • bytes: Memory used
  • curr_items: Current items

Hit Rate

Calculate:

hit_rate = hits / (hits + misses) * 100

Target:

  • 80%+ hit rate
  • Monitor and optimize
  • Adjust TTL if needed

Performance Characteristics

Maximum Read & Write Throughput

Single Node:

  • Max Read Throughput: 200K-500K ops/sec (GET operations)
  • Max Write Throughput: 200K-500K ops/sec (SET operations)
  • Typical Performance: 100K-300K ops/sec for mixed workloads

Distributed Cluster:

  • Max Read Throughput: 200K-500K ops/sec per node (linear scaling)
  • Max Write Throughput: 200K-500K ops/sec per node (linear scaling)
  • Example: 10-node cluster can handle 2M-5M ops/sec total

Factors Affecting Throughput:

  • Network latency
  • Item size (larger items = lower throughput)
  • Memory bandwidth
  • CPU cores (multi-threaded)
  • Connection pooling
  • Key distribution across nodes

Performance Comparison:

  • Memcached: Typically faster than Redis for simple GET/SET operations
  • Redis: More features but slightly slower for basic operations
  • Memcached: Optimized for maximum throughput on simple operations

Latency

Typical Latency:

  • Local Network: < 0.5ms
  • Same Data Center: 0.5-2ms
  • Cross-Region: 5-50ms

Factors Affecting Latency:

  • Network latency
  • Item size
  • Server load
  • Memory access speed

Best Practices

1. Key Design

  • Use descriptive prefixes
  • Include identifiers
  • Keep keys short
  • Use consistent naming

2. TTL Strategy

  • Set appropriate TTL
  • Balance freshness vs performance
  • Use longer TTL for stable data
  • Shorter TTL for dynamic data

3. Error Handling

  • Handle cache failures gracefully
  • Fallback to database
  • Don’t let cache errors break app
  • Log cache errors

4. Memory Management

  • Monitor memory usage
  • Set appropriate limits
  • Use LRU eviction
  • Plan for growth

Comparison with Redis

Memcached vs Redis

Memcached:

  • Simple key-value
  • No persistence
  • No data structures
  • Faster for simple operations

Redis:

  • Rich data structures
  • Persistence options
  • More features
  • Slightly slower

When to Use Memcached:

  • Simple caching needs
  • Maximum performance
  • No persistence needed
  • Simple key-value only

When to Use Redis:

  • Need data structures
  • Persistence required
  • More features needed
  • Complex operations

What Interviewers Look For

Caching Understanding

  1. Cache Concepts
    • Understanding of caching benefits
    • Cache strategies
    • Cache invalidation
    • Red Flags: No caching understanding, wrong strategies, no invalidation
  2. Performance Optimization
    • Cache hit rate optimization
    • TTL strategy
    • Key design
    • Red Flags: No optimization, poor hit rate, bad keys
  3. Distributed Caching
    • Consistent hashing
    • Multi-server setup
    • Load distribution
    • Red Flags: Single server, no hashing, poor distribution

Problem-Solving Approach

  1. Cache Strategy Selection
    • Choose appropriate strategy
    • Cache-aside vs write-through
    • Trade-off analysis
    • Red Flags: Wrong strategy, no trade-offs, poor selection
  2. Cache Invalidation
    • TTL vs manual
    • Invalidation on updates
    • Cache warming
    • Red Flags: No invalidation, stale data, no warming

System Design Skills

  1. Performance Optimization
    • Cache layer design
    • Hit rate optimization
    • Memory management
    • Red Flags: No cache, poor performance, no optimization
  2. Scalability
    • Distributed caching
    • Horizontal scaling
    • Load distribution
    • Red Flags: Single server, no scaling, poor distribution

Communication Skills

  1. Clear Explanation
    • Explains caching concepts
    • Discusses trade-offs
    • Justifies design decisions
    • Red Flags: Unclear explanations, no justification, confusing

Meta-Specific Focus

  1. Performance Expertise
    • Understanding of caching
    • Memcached mastery
    • Performance optimization
    • Key: Demonstrate performance expertise
  2. System Design Skills
    • Can design cache layer
    • Understands performance impact
    • Makes informed trade-offs
    • Key: Show practical caching design skills

Summary

Memcached Key Points:

  • In-Memory Caching: Fast key-value storage in RAM
  • Distributed: Multiple servers with consistent hashing
  • Simple API: Easy to integrate and use
  • High Performance: Sub-millisecond latency
  • Cache Strategies: Cache-aside, write-through, write-back
  • LRU Eviction: Automatic memory management

Common Use Cases:

  • Database query caching
  • Session storage
  • API response caching
  • Frequently accessed data
  • Performance optimization

Best Practices:

  • Use descriptive cache keys
  • Set appropriate TTL
  • Implement cache-aside pattern
  • Monitor cache hit rate
  • Handle cache failures gracefully
  • Use connection pooling
  • Design for cache invalidation

Memcached is a powerful caching solution that significantly improves application performance by reducing database load and response times.