Memcached: In-Memory Caching for High Performance

Memcached is a high-performance, distributed in-memory caching system used to store frequently accessed data. It reduces database queries, lowers latency, and helps scale web applications by caching data in RAM.

Memcached: High-Performance Distributed Caching

Memcached is a high-performance, distributed memory object caching system. It is designed to speed up dynamic web applications by alleviating database load. Memcached stores key-value pairs of data in RAM, reducing the number of times an external data source (such as a database or API) must be read. It is simple yet powerful, making it one of the most widely used caching solutions in the world.

Memcached is used by some of the largest websites on the internet, including Facebook, Wikipedia, Twitter, and YouTube. Its simplicity, speed, and scalability have made it a go-to solution for applications that need to cache database query results, API responses, session data, and other frequently accessed information. To understand Memcached properly, it is helpful to be familiar with concepts like database caching, Redis, distributed systems, and web performance optimization.

What Is Memcached

Memcached is an open-source, high-performance, distributed memory object caching system. It is used to cache data and objects in RAM to reduce the number of times an external data source (such as a database or API) must be accessed. Memcached is a key-value store, meaning data is stored as pairs of keys and values, and retrieved using those keys.

  • In-Memory Storage: All data is stored in RAM, providing microsecond latency.
  • Distributed Architecture: Multiple Memcached servers can work together as a single logical cache.
  • Simple Key-Value Store: No query language, no data types beyond strings and integers.
  • Least Recently Used (LRU) Eviction: Automatically removes old data when memory is full.
  • Volatile Storage: Data is not persisted; restarting Memcached clears all data.
  • No Replication: Each server operates independently; no built-in replication or clustering.
Memcached architecture diagram:
┌─────────────────────────────────────────────────────────────┐
│                    Application Servers                       │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐        │
│  │ Server1 │  │ Server2 │  │ Server3 │  │ Server4 │        │
│  └────┬────┘  └────┬────┘  └────┬────┘  └────┬────┘        │
│       │            │            │            │              │
│       └────────────┴────────────┴────────────┘              │
│                            │                                 │
│                            ▼                                 │
│              ┌─────────────────────────┐                    │
│              │   Consistent Hashing    │                    │
│              │    Key Distribution     │                    │
│              └─────────────────────────┘                    │
│                            │                                 │
│       ┌────────────────────┼────────────────────┐           │
│       ▼                    ▼                    ▼           │
│  ┌─────────┐          ┌─────────┐          ┌─────────┐     │
│  │Memcached│          │Memcached│          │Memcached│     │
│  │ Server1 │          │ Server2 │          │ Server3 │     │
│  │  (RAM)  │          │  (RAM)  │          │  (RAM)  │     │
│  └─────────┘          └─────────┘          └─────────┘     │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Why Memcached Matters

Memcached addresses one of the most common performance bottlenecks in web applications: database access. By caching frequently accessed data in memory, Memcached dramatically reduces response times and database load.

  • Reduced Database Load: Cache query results, reducing the number of database queries by 80-95%.
  • Ultra-Low Latency: Memory access is microseconds compared to milliseconds for database queries.
  • Horizontal Scalability: Add more Memcached servers to increase cache capacity and throughput.
  • Simple API: Easy to learn and implement with only a few commands (get, set, delete).
  • Language Support: Client libraries available for all major programming languages.
  • Proven Reliability: Used by the world's largest websites for over a decade.
  • Cost Effective: Reduces database infrastructure costs by handling read-heavy workloads.

Installing Memcached

Memcached is available on all major operating systems and can be installed using package managers or compiled from source.

Installation commands:
# Ubuntu / Debian
sudo apt update
sudo apt install memcached
sudo apt install libmemcached-tools

# CentOS / RHEL / Fedora
sudo yum install memcached
sudo yum install libmemcached

# macOS (Homebrew)
brew install memcached

# Windows (unofficial)
# Download from https://github.com/nono303/memcached/releases
# Or use WSL (Windows Subsystem for Linux)

# Start Memcached service
sudo systemctl start memcached
sudo systemctl enable memcached

# Verify installation
memcached-tool localhost:11211 stats
Basic Memcached configuration (/etc/memcached.conf):
# Listen on all network interfaces (default: localhost)
-l 0.0.0.0

# Port to listen on (default: 11211)
-p 11211

# Maximum memory to use (in MB)
-m 1024

# Maximum connections
-c 1024

# Number of threads to use
-t 4

# User to run as
-u memcache

# Verbose logging (for debugging)
# -v

# Enable large memory pages
# -L

Basic Memcached Operations

Memcached provides a simple set of commands for storing and retrieving data. The protocol is text-based, making it easy to understand and debug.

Basic commands via telnet:
# Connect to Memcached
telnet localhost 11211

# Set a value (store with key "user:123", value "John Doe")
set user:123 0 3600 8
John Doe
STORED

# Get a value
get user:123
VALUE user:123 0 8
John Doe
END

# Add (only if key doesn't exist)
add newkey 0 3600 5
hello
STORED

# Replace (only if key exists)
replace user:123 0 3600 9
John Smith
STORED

# Append to existing value
append user:123 0 3600 6
 Jones
STORED

# Prepend to existing value
prepend user:123 0 3600 3
Mr. 
STORED

# Get multiple values
get user:123 newkey
VALUE user:123 0 15
Mr. John Smith Jones
END

# Delete a key
delete user:123
DELETED

# Increment (for counters)
set counter 0 3600 1
5
STORED
incr counter 2
7
decr counter 1
6

# Flush all data (be careful!)
flush_all
OK

Using Memcached with PHP

PHP has excellent Memcached support through the memcached extension (preferred) or memcache extension.

PHP Memcached example:
<?php
// Connect to Memcached
$memcached = new Memcached();
$memcached->addServer('localhost', 11211);
// Add multiple servers for redundancy
$memcached->addServers([
    ['server1', 11211],
    ['server2', 11211],
    ['server3', 11211]
]);

// Set cache key
$key = 'user_profile_123';
$ttl = 3600; // 1 hour

// Try to get from cache
$profile = $memcached->get($key);

if ($memcached->getResultCode() === Memcached::RES_NOTFOUND) {
    // Cache miss - query database
    $profile = $db->query("SELECT * FROM users WHERE id = 123");
    
    // Store in cache
    $memcached->set($key, $profile, $ttl);
}

// Use the data
echo "User name: " . $profile['name'];

// Delete from cache when data changes
$memcached->delete('user_profile_123');

// Store complex data (arrays, objects)
$userData = [
    'id' => 123,
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'preferences' => ['theme' => 'dark', 'language' => 'en']
];
$memcached->set('user_data_123', $userData, 3600);
$retrieved = $memcached->get('user_data_123');

// Atomic operations
$memcached->increment('page_views', 1);
$memcached->decrement('stock_count', 1);

Using Memcached with Python

Python has several Memcached clients, with python-memcached being the most popular.

Python Memcached example:
import memcache
import json

# Connect to Memcached
client = memcache.Client(['127.0.0.1:11211'], debug=0)

# Set a value
client.set('user:123', 'John Doe', time=3600)

# Get a value
user = client.get('user:123')
print(user)  # John Doe

# Store complex data (serialize to JSON)
user_data = {
    'id': 123,
    'name': 'John Doe',
    'email': 'john@example.com'
}
client.set('user_data:123', json.dumps(user_data), 3600)

# Retrieve and deserialize
data = json.loads(client.get('user_data:123'))

# Atomic increment/decrement
client.incr('page_views', 1)
client.decr('stock_count', 1)

# Multi-get (efficient for multiple keys)
keys = ['user:123', 'user:456', 'user:789']
users = client.get_multi(keys)

# Delete key
client.delete('user:123')

# Check if key exists
if client.get('some_key') is None:
    print('Key not found')

Using Memcached with Node.js

Node.js can use Memcached through the memcached npm package.

Node.js Memcached example:
const Memcached = require('memcached');

// Connect to Memcached
const memcached = new Memcached('localhost:11211');

// Set a value
memcached.set('user:123', 'John Doe', 3600, (err) => {
    if (err) console.error('Error setting cache:', err);
});

// Get a value
memcached.get('user:123', (err, data) => {
    if (err) {
        console.error('Error getting cache:', err);
    } else if (data) {
        console.log('Cache hit:', data);
    } else {
        console.log('Cache miss');
    }
});

// Store object (automatically serialized)
const userProfile = {
    id: 123,
    name: 'John Doe',
    email: 'john@example.com',
    preferences: { theme: 'dark' }
};
memcached.set('profile:123', userProfile, 3600);

// Increment/Decrement
memcached.incr('counter', 1, (err, result) => {
    console.log('New counter value:', result);
});

// Delete key
memcached.del('user:123', (err) => {
    if (err) console.error('Delete failed:', err);
});

// Multi-get
memcached.getMulti(['key1', 'key2', 'key3'], (err, data) => {
    console.log('Multiple keys:', data);
});

// Close connection
memcached.end();

Caching Strategies with Memcached

Effective caching requires choosing the right strategy. Memcached is well-suited for several common caching patterns.

Strategy Description Use Case
Cache-Aside (Lazy Loading) Application checks cache first, loads from database on miss Most read-heavy applications
Write-Through Application writes to cache and database simultaneously Write-heavy workloads needing consistency
Write-Behind Application writes to cache, database updated asynchronously High-write throughput, eventual consistency acceptable
Object Caching Complete objects (user profiles, product data) stored in cache Entity caching, reducing database joins
Query Result Caching Database query results stored with query hash as key Expensive queries with low update frequency
Session Storage PHP sessions stored in Memcached for shared session handling Load-balanced environments
Query result caching example:
function getPopularProducts($memcached) {
    // Create cache key from query
    $cacheKey = 'popular_products_v1';
    
    // Try cache
    $products = $memcached->get($cacheKey);
    
    if ($memcached->getResultCode() === Memcached::RES_NOTFOUND) {
        // Cache miss - run expensive query
        $products = $db->query("
            SELECT p.*, COUNT(o.id) as order_count
            FROM products p
            LEFT JOIN orders o ON p.id = o.product_id
            GROUP BY p.id
            ORDER BY order_count DESC
            LIMIT 10
        ");
        
        // Cache for 5 minutes
        $memcached->set($cacheKey, $products, 300);
    }
    
    return $products;
}

// Invalidate cache when data changes
function updateProduct($productId, $data) {
    $db->update('products', $data, ['id' => $productId]);
    
    // Clear related caches
    $memcached->delete('popular_products_v1');
    $memcached->delete("product_{$productId}");
}

Memcached vs Redis

Memcached and Redis are both popular in-memory data stores, but they have different strengths and use cases.

Feature Memcached Redis
Data Types Simple key-value (strings only) Rich types (strings, hashes, lists, sets, sorted sets, streams)
Persistence None (volatile memory only) RDB snapshots, AOF logs
Replication None (each server independent) Master-replica replication
Clustering Client-side consistent hashing Native cluster support (Redis Cluster)
Atomic Operations Increment/decrement only Rich atomic operations on data structures
Lua Scripting Not supported Full Lua scripting support
Pub/Sub No Built-in pub/sub messaging
Use Case Simple caching, session storage Complex data structures, queues, real-time analytics, caching
When to choose Memcached vs Redis:
Choose Memcached when:
- You need simple key-value caching only
- You want maximum simplicity
- You have a very high read/write ratio
- You don't need persistence or replication
- You are using it for database query result caching

Choose Redis when:
- You need complex data structures (lists, sets, hashes)
- You need persistence (data survives restarts)
- You need replication or clustering
- You need atomic operations on collections
- You need pub/sub messaging
- You need Lua scripting

Scaling Memcached

Memcached scales horizontally by adding more servers. Client libraries use consistent hashing to distribute keys across servers.

Consistent hashing implementation (PHP):
// Configure multiple servers
$memcached = new Memcached();
$memcached->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
$memcached->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);

// Add servers
$servers = [
    ['cache1.example.com', 11211],
    ['cache2.example.com', 11211],
    ['cache3.example.com', 11211],
    ['cache4.example.com', 11211]
];
$memcached->addServers($servers);

// Keys are automatically distributed across servers
// When you add or remove servers, minimal keys are remapped

// Adding a new server (only ~1/N keys redistributed)
$servers[] = ['cache5.example.com', 11211];
$memcached->addServers($servers);
Server weight configuration:
// Assign weights for uneven server capacities
$servers = [
    ['cache1.example.com', 11211, 50],  // weight 50
    ['cache2.example.com', 11211, 30],  // weight 30
    ['cache3.example.com', 11211, 20]   // weight 20
];
$memcached->addServers($servers);

// Heavier servers handle more keys proportionally

Memcached Monitoring

Monitoring Memcached helps you understand cache effectiveness and identify issues.

Key metrics to monitor:
# Get statistics
echo "stats" | nc localhost 11211

# Important metrics:
# curr_items - Current number of items in cache
# total_items - Total items stored since server start
# get_hits - Number of successful get requests
# get_misses - Number of failed get requests
# hit_ratio - get_hits / (get_hits + get_misses)
# evictions - Items removed due to memory pressure
# curr_connections - Current open connections
# bytes - Current memory usage
# limit_maxbytes - Maximum allowed memory

# Using memcached-tool
memcached-tool localhost:11211 display
memcached-tool localhost:11211 stats
memcached-tool localhost:11211 dump

# Hit ratio calculation
Hit Ratio = get_hits / (get_hits + get_misses) * 100
# Target: > 80% for effective caching

# Memory usage
Memory Usage = bytes / limit_maxbytes * 100
# Target: < 90% to avoid evictions
Monitoring script (Python):
import memcache
import time

client = memcache.Client(['127.0.0.1:11211'])

def get_cache_stats():
    stats = client.get_stats()
    if stats:
        for server, data in stats[0].items():
            print(f"{server}: {data}")

def get_hit_ratio():
    stats = client.get_stats()
    if stats:
        data = stats[0]
        hits = int(data.get(b'get_hits', 0))
        misses = int(data.get(b'get_misses', 0))
        total = hits + misses
        if total > 0:
            ratio = (hits / total) * 100
            return ratio
    return 0

# Monitor every 60 seconds
while True:
    ratio = get_hit_ratio()
    print(f"Hit Ratio: {ratio:.2f}%")
    time.sleep(60)

Common Memcached Mistakes to Avoid

Even experienced developers make mistakes when implementing Memcached. Being aware of these common pitfalls helps you build effective caching strategies.

  • Caching Too Much Data: Storing large objects consumes memory and causes evictions of frequently accessed data.
  • No Expiration Policy: Caches without TTL grow indefinitely and serve stale data.
  • Cache Stampede: Many requests simultaneously missing cache and regenerating data. Use locking or stale-while-revalidate.
  • Inconsistent Keys: Different parts of application using different key formats leads to cache misses.
  • Ignoring Serialization Overhead: Complex object serialization can be expensive. Use simple data structures when possible.
  • Single Point of Failure: Using only one Memcached server creates a single point of failure. Use multiple servers.
  • Not Monitoring: Without monitoring, you cannot know hit ratios or eviction rates.
  • Cache Penetration: Requests for non-existent keys bypass cache. Cache null values for negative results.

Frequently Asked Questions

  1. Is Memcached persistent?
    No. Memcached stores all data in RAM and does not persist to disk. Restarting Memcached clears all data. This is by design for maximum performance.
  2. What happens when Memcached runs out of memory?
    Memcached uses an LRU (Least Recently Used) algorithm to evict old items when memory is full. The least recently accessed items are removed first.
  3. Can Memcached be used for session storage?
    Yes. Memcached is commonly used for shared session storage in load-balanced environments. PHP supports Memcached session handlers natively.
  4. What is the maximum key and value size?
    Keys are limited to 250 bytes. Values are limited to 1MB by default (configurable). For larger data, consider splitting across multiple keys or using Redis.
  5. How does Memcached distribute data across servers?
    Memcached clients use consistent hashing to determine which server stores each key. This minimizes key redistribution when servers are added or removed.
  6. What should I learn next after understanding Memcached?
    After mastering Memcached, explore Redis for advanced data structures and persistence, database caching strategies, CDN caching, and performance optimization for comprehensive application acceleration.

Conclusion

Memcached is a simple, powerful, and battle-tested caching solution that has powered some of the largest websites on the internet. Its straightforward key-value API, distributed architecture, and ultra-low latency make it an excellent choice for reducing database load and accelerating web applications.

While Memcached lacks features like persistence, replication, and complex data structures found in Redis, its simplicity is also its strength. For pure caching workloads, Memcached is often the best choice. It is easy to operate, scales horizontally, and has client libraries for virtually every programming language.

Effective caching with Memcached requires careful strategy: choose appropriate TTLs, handle cache misses gracefully, invalidate caches when data changes, and monitor hit ratios. When implemented correctly, Memcached can reduce database load by 80-95% and cut response times from milliseconds to microseconds.

To deepen your understanding, explore related topics like Redis for advanced caching, database caching strategies, CDN caching for edge delivery, and performance optimization for comprehensive application acceleration. Together, these skills form a complete foundation for building fast, scalable web applications.