Memcached’s get command is great for fetching a single item, but if you’re fetching many items, one get after another is a performance killer. The get command is a request-response operation: send request, wait for response, send next request, wait for response. This round-trip latency adds up fast.
Here’s what a high-throughput get operation looks like when you’re fetching 100 items, one by one:
# Simulating fetching 100 items individually
for i in {1..100}; do
echo "get mykey_$i" | nc memcached.example.com 11211
sleep 0.001 # Simulate some minimal processing time
done
This script might take seconds. Now, let’s look at the same operation using memcached’s get command, which is designed for batching:
# Fetching 100 items using a single multi-get
echo -e "get mykey_1\nget mykey_2\nget mykey_3\n...\nget mykey_100" | nc memcached.example.com 11211
This single network round trip will likely complete in milliseconds, even for hundreds of keys. The magic is in how Memcached handles multiple get requests sent in a single TCP connection.
The core problem multi-get solves is network latency. Each individual get request involves:
- Client sending the request.
- Memcached server receiving the request.
- Memcached server processing the request.
- Memcached server sending the response.
- Client receiving the response.
When you have many get requests, you’re repeating steps 1 through 5 for each item. This is called "head-of-line blocking" at the network level. The client can’t send the next request until the current one is fully acknowledged.
The multi-get command, however, allows the client to send multiple get commands back-to-back within a single TCP connection. Memcached reads all these commands from the socket, processes them, and then sends back all the corresponding responses in one go. This dramatically reduces the number of network round trips from N (where N is the number of items) down to just 1.
Here’s how you construct a multi-get request. You simply send multiple get commands, each terminated by a newline (\n), followed by an empty line to signal the end of the command block.
get key1
get key2
get key3
END
Memcached will respond with the values for each key, prefixed by VALUE key flags bytes. If a key is not found, it will simply not have a corresponding VALUE line. The response also ends with END.
VALUE key1 0 10
value1
VALUE key2 0 12
value2
END
The flags are typically set by the client application when storing the data and are returned with the get command. The bytes field indicates the size of the value in bytes.
The maximum number of keys you can include in a single multi-get request is configurable on the Memcached server, but the default is usually 1000. Exceeding this limit will result in an error. You can check this limit with memcached -h or by looking at the server’s configuration.
The multi-get operation is atomic from the perspective of the Memcached server’s internal state within that single request. However, it’s crucial to understand that the data could have been modified by other clients between the time you initiated your multi-get and when the server processes it, or between individual get commands if you were doing them serially. Memcached doesn’t offer transaction semantics for multi-get; it’s purely an optimization for fetching multiple items efficiently.
When Memcached processes a multi-get, it reads all the get requests from the buffer, queues them up for retrieval, and then writes all the corresponding VALUE or NOT_FOUND responses back out. This internal processing order means that if you request keyA and keyB, and keyA is not found but keyB is, the response will accurately reflect this:
NOT_FOUND
VALUE keyB 0 5
valueB
END
The most surprising mechanical detail of multi-get is that Memcached doesn’t actually have a distinct multi_get command in its protocol. It simply processes a sequence of get commands sent over the same connection until it sees the END marker. This means that any client library that can send multiple get commands consecutively and handle the streaming response can implement multi-get functionality, even if the protocol itself doesn’t have a specific keyword for it.
The next optimization you’ll likely encounter when dealing with Memcached at scale is understanding how to effectively use mget in conjunction with consistent hashing for distributed cache environments.