Memcached’s design is so minimal that it doesn’t even store keys beyond the data itself; it’s just a giant hash table mapping byte strings to byte strings.
Let’s see it in action. Imagine you’re building a simple URL shortener. You need to store a mapping from a short URL (the key) to a long URL (the value).
# Start a Memcached server (default port is 11211)
memcached -p 11211
# Add a key-value pair: short URL -> long URL
echo "set short.ly/abc 0 3600 50" | nc localhost 11211
echo "http://www.example.com/very/long/url/that/needs/shortening" | nc localhost 11211
# Memcached responds with 'STORED'
# Retrieve the value associated with the key
echo "get short.ly/abc" | nc localhost 11211
# Memcached responds with:
# VALUE short.ly/abc 0 50
# http://www.example.com/very/long/url/that/needs/shortening
# END
# Increment a counter (e.g., for click tracking)
echo "incr short.ly/abc_clicks 1" | nc localhost 11211
# Memcached responds with '1'
echo "get short.ly/abc_clicks" | nc localhost 11211
# Memcached responds with:
# VALUE short.ly/abc_clicks 0 1
# 1
# END
This simplicity is Memcached’s superpower. It’s a pure, unadulterated key-value store optimized for speed. It doesn’t care about data types, complex structures, or persistence. You give it bytes, it gives you bytes back, as fast as humanly possible.
Redis, on the other hand, is a "data structure server." It also stores keys mapping to values, but those values can be more than just raw strings. Redis supports lists, sets, sorted sets, hashes, bitmaps, hyperloglogs, and streams. This means you can perform more complex operations directly on the server, without fetching all the data to your application and processing it there.
Consider tracking user activity on a website. With Redis, you could use a SET to store unique active users for a given hour, or a ZSET (sorted set) to store page views with timestamps as scores, allowing you to easily query the most popular pages in the last 5 minutes.
# Start a Redis server (default port is 6379)
redis-server --port 6379
# Add a simple key-value pair (like Memcached)
SET mykey "myvalue"
# -> OK
GET mykey
# -> "myvalue"
# Add a list
LPUSH mylist "item1"
# -> (integer) 1
LPUSH mylist "item2"
# -> (integer) 2
LRANGE mylist 0 -1
# -> 1) "item2"
# 2) "item1"
# Add a set of unique items
SADD myset "user1"
# -> (integer) 1
SADD myset "user2"
# -> (integer) 1
SADD myset "user1" # Adding a duplicate has no effect
# -> (integer) 0
SMEMBERS myset
# -> 1) "user2"
# 2) "user1"
# Add a sorted set (e.g., for leaderboards or recent activity)
ZADD leaderboard 100 "playerA"
# -> (integer) 1
ZADD leaderboard 150 "playerB"
# -> (integer) 1
ZADD leaderboard 120 "playerC"
# -> (integer) 1
ZREVRANGE leaderboard 0 -1 WITHSCORES
# -> 1) "playerB"
# 2) "150"
# 3) "playerC"
# 4) "120"
# 5) "playerA"
# 6) "100"
The core difference boils down to abstraction. Memcached is a raw byte-level cache; it doesn’t know or care what your data means. Redis understands its data types and offers commands to manipulate them server-side. This means Redis can do more complex work with less network round-trips and less CPU on your application servers, but at the cost of slightly more memory overhead per key and a more complex feature set to learn.
When you’re dealing with simple object caching—storing serialized data structures or HTML fragments where the primary goal is just to get them in and out quickly—Memcached often excels. Its simplicity means less overhead, and its multi-threaded architecture can be very efficient on multi-core CPUs for high-volume, simple GET/SET operations.
However, if you need to maintain collections of data, perform atomic operations on counters, implement queues, or manage leaderboards directly on the server, Redis is the clear winner. Its rich data types allow you to model complex application logic within the cache itself, reducing the burden on your application code and database.
A common misconception is that Redis is always slower than Memcached. While Memcached might have a slight edge on raw, single-threaded performance for simple GETs due to its bare-bones nature, Redis’s ability to perform complex operations server-side often makes it faster for those specific use cases because it avoids moving large amounts of data back and forth between the application and the cache. Furthermore, Redis’s single-threaded nature for command execution, while sometimes seen as a limitation, actually simplifies its internal workings and avoids many of the locking complexities that can plague multi-threaded systems, leading to predictable performance.
The next step after choosing between these two is understanding how to leverage their persistence options for disaster recovery and warm restarts.