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.
┌─────────────────────────────────────────────────────────────┐
│ 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.
# 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
# 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.
# 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
// 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.
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.
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 |
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 |
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.
// 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);
// 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.
# 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
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
- 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. - 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. - 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. - 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. - 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. - 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.
