How Does Redis Understand Your Commands?
When you fire a command like:
SET username aliceRedis doesn’t magically understand English. Your client needs to translate it into a format Redis can parse. This is where RESP comes in.
RESP stands for REdis Serialization Protocol. It’s the language that clients and servers speak to understand each other.
Without a standardized protocol, every Redis client (Node.js, Python, Go, etc.) would need its own custom way to communicate. Chaos.
Instead, Redis defined RESP, and every client follows it. Simple. Elegant.
Part 1: What Is RESP?
The Basic Idea
When you send a command to Redis, it needs to be serialized (converted into a specific format). RESP is that format.
Example:
You want to execute:
SET key valueYour client serializes it as:
*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\nLet’s break this down:
*3= “This is an array with 3 elements”$3= “Next element is a bulk string with 3 bytes”SET= The actual command$3= “Next element is 3 bytes”key= The actual key$5= “Next element is 5 bytes”value= The actual value\r\n= Line terminator (CRLF) after every line
Redis reads this, understands it’s a 3-element array, parses each bulk string, and executes the command.
Why RESP?
Protocol Requirements:
- Must be unambiguous (no guessing)
- Must be fast to parse
- Should handle binary data
- Should be reasonably human-readable
RESP nails all of these. It’s the opposite of overly complex formats. It’s simple, fast, and efficient.
Part 2: RESP Data Types
RESP supports several data types. Each one starts with a special character that tells Redis what to expect.
1. Simple Strings
Format: +<string>\r\n
Starts with: + (plus sign)
Example:
+OK\r\nRedis sends this when a command succeeds. OK means the operation was successful.
Another example:
+PONG\r\nWhen you send PING, Redis responds with +PONG\r\n.
Overhead: A simple string response requires n + 3 bytes (string length + 1 for + + 2 for \r\n).
When to use: Quick confirmations, status messages. Minimal overhead.
Note
Important: Simple strings can’t contain \r\n because that’s the terminator. If you need to send arbitrary text with line breaks, use bulk strings instead.
2. Integers
Format: :<integer>\r\n
Starts with: : (colon)
Example:
:1729\r\nWhen you execute INCR counter, Redis increments the counter and responds with the new value as an integer.
Real example:
ZADD leaderboard 100 alice// Redis responds: :1\r\n (meaning 1 element was added)
GET counter// Redis responds: :5\r\n (the counter value is 5)When to use: Counts, scores, ranks, any numeric response.
3. Bulk Strings
Format: $<length>\r\n<data>\r\n
Starts with: $ (dollar sign)
Example:
$4\r\nPONG\r\nBreaking it down:
$4= “The following data is 4 bytes”PONG= The actual data\r\n= Terminator
Another example:
$11\r\nHello World\r\nWhy Bulk Strings When Simple Strings Exist?
- Binary safety: Bulk strings can contain ANY byte, including
\r\n. Perfect for storing images, binary data, or text with special characters. - Known length: The
$4tells the parser exactly how many bytes to read. No ambiguity. - Flexibility: Simple strings fail when data contains the terminator. Bulk strings don’t.
Example of Why Binary Safety Matters:
// If you tried to send this as a simple string:+ This is a\r\ntricky string\r\n
// The parser would think it's done after the first \r\n// It would read "This is a" and stop
// But as a bulk string:$18\r\nThis is a\r\ntricky string\r\n
// The parser knows to read exactly 18 bytes, even if they contain \r\nSpecial Bulk Strings
Empty string:
$0\r\n\r\n“Zero bytes of data, followed by terminator.”
Null string (absence of data):
$-1\r\nThe length -1 is a special sentinel value meaning “no data exists.” Used when a key doesn’t exist, for example.
GET nonexistent_key// Redis responds: $-1\r\n (key not found)When to use: Storing text, binary data, images, or any response from Redis that isn’t a simple status message.
4. Arrays
Format: *<count>\r\n<elements>\r\n
Starts with: * (asterisk)
Example:
*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\nThis is an array with 3 elements:
- “SET”
- “key”
- “value”
Real Redis Response Example:
When you execute MGET key1 key2 key3, Redis returns an array of values:
*3\r\n$5\r\nvalue1\r\n$5\r\nvalue2\r\n$5\r\nvalue3\r\nThis means: “Array with 3 elements: ‘value1’, ‘value2’, ‘value3’.”
Empty Array
*0\r\n“An array with zero elements.” Rare in practice.
Null Array
*-1\r\n“No array exists.” Similar to null string, but for arrays.
Nested Arrays
Arrays can contain other arrays (or any RESP type):
*2\r\n*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n*2\r\n$4\r\nhello\r\n$5\r\nworld\r\nThis is:
[ ["foo", "bar"], ["hello", "world"]]When to use: Returning multiple values, bulk operations, results from scanning, etc.
Note
Parser Trick: Because arrays have a prefix length (*3 means 3 elements), the parser knows exactly how many elements to expect. It doesn’t have to guess when the array ends. This makes parsing fast and unambiguous.
5. Errors
Format: -<error message>\r\n
Starts with: - (hyphen)
Example:
-ERR unknown command 'SET'\r\nor
-WRONGTYPE Operation against a key holding the wrong kind of value\r\nWhen Redis encounters an error, it responds with a message prefixed by -. The client detects the - and knows it’s an error.
When to use: When something goes wrong—syntax error, wrong key type, permission denied, etc.
Part 3: RESP in Action
Example 1: Setting a Key-Value Pair
You execute:
SET username aliceClient serializes (RESP):
*3\r\n$3\r\nSET\r\n$8\r\nusername\r\n$5\r\nalice\r\nRedis parses:
*3→ Expecting 3 elements$3\r\nSET\r\n→ First element: bulk string “SET”$8\r\nusername\r\n→ Second element: bulk string “username”$5\r\nalice\r\n→ Third element: bulk string “alice”
Redis executes: Stores username = alice in memory
Redis responds:
+OK\r\nExample 2: Getting a Non-Existent Key
You execute:
GET nonexistentClient serializes:
*2\r\n$3\r\nGET\r\n$11\r\nnonexistent\r\nRedis responds (key not found):
$-1\r\nThe -1 tells the client: “This key doesn’t exist.”
Example 3: Getting Multiple Keys
You execute:
MGET key1 key2 key3Client serializes:
*4\r\n$4\r\nMGET\r\n$4\r\nkey1\r\n$4\r\nkey2\r\n$4\r\nkey3\r\nRedis responds (assuming key1=“value1”, key2=“value2”, key3 doesn’t exist):
*3\r\n$6\r\nvalue1\r\n$6\r\nvalue2\r\n$-1\r\nAn array with 3 elements: “value1”, “value2”, and null (key3 doesn’t exist).
Part 4: Why RESP Is Brilliant
1. Human-Readable
*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\nIf you print this to the console, you can understand it. Compare this to binary protocols where you see hex dumps. RESP is debuggable.
2. Simple to Parse
Each line starts with a type indicator (+, :, $, *, -). The parser doesn’t need complex logic. Read the first character, figure out what type it is, parse accordingly.
No complex state machines. No ambiguity.
3. Performant
- Prefix length: You know upfront how many bytes to read (
$4= read 4 bytes). No scanning for the end. - Linear parsing: Read line by line, process, move on. No backtracking.
- Minimal overhead: A simple string response is just
+PONG\r\n(7 bytes). Compare to JSON:
{"status": "success"} // 22 bytesRESP wins for small responses.
4. Binary Safe
Bulk strings can contain any byte sequence, including \r\n. This is critical for storing images, PDFs, serialized objects, etc.
Part 5: RESP Version 2 vs RESP3
Redis introduced RESP3 in version 6.0 to support additional data types (sets, maps, etc. as distinct types, not just arrays).
However, RESP2 is still widely used and understood. For this discussion, we focused on RESP2.
Key difference:
- RESP2: Simpler, older, still predominant
- RESP3: Supports more native data types, better for clients
Part 6: How This Matters in Practice
When You Use a Redis Client Library
You never manually construct RESP messages. Your client library does it for you.
Node.js example:
const redis = require('redis');const client = redis.createClient();
client.set('username', 'alice', (err, reply) => { // Under the hood, the client serialized to RESP, // sent it to Redis, received the response, // and deserialized it back to JavaScript});The library abstracts away RESP. But knowing it exists helps you understand:
- Why certain data types exist – RESP supports strings, integers, arrays, and errors, so Redis clients mirror these
- Why binary safety matters – Bulk strings enable it
- Why responses are fast – RESP is minimal overhead
When Debugging
If you’re using redis-cli or debugging a network issue, you might see RESP directly:
$ nc localhost 6379*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n+OK\r\nUnderstanding RESP helps you read these raw messages.
Part 7: Comparing RESP to Other Protocols
RESP vs JSON
JSON:
{ "command": "SET", "key": "username", "value": "alice"}Problems:
- Larger (more bytes)
- Requires parsing (quote detection, escape handling)
- Not binary-safe (need Base64 encoding for binary data)
- Overhead for nested structures
RESP:
*3\r\n$3\r\nSET\r\n$8\r\nusername\r\n$5\r\nalice\r\nAdvantages:
- Compact
- Fast to parse (no complex logic)
- Binary-safe
- Minimal overhead
For a high-performance protocol like Redis, RESP is superior.
RESP vs gRPC
gRPC uses Protocol Buffers (binary, typed, more complex).
RESP is simpler, text-based, and more human-debuggable.
The trade-off: gRPC is better for large-scale distributed systems; RESP is better for simplicity and speed in a single service.
Part 8: Summary: The RESP Protocol
| Type | Start | Format | Example |
|---|---|---|---|
| Simple String | + | +OK\r\n | Confirmations, simple responses |
| Error | - | -ERR message\r\n | Error messages |
| Integer | : | :42\r\n | Counts, scores, numeric responses |
| Bulk String | $ | $3\r\nSET\r\n | Text, binary data, large strings |
| Array | * | *2\r\n$3\r\nkey\r\n$5\r\nvalue\r\n | Multiple values, complex responses |
Key Principles:
- Every RESP message is unambiguous
- Prefix length enables fast, non-blocking parsing
- Binary safe (bulk strings)
- Human-readable
- Minimal overhead
Conclusion
RESP is the foundation of Redis communication. It’s not something you usually think about when using Redis—your client library handles it automatically.
But understanding RESP:
- Demystifies how clients and servers talk
- Helps debug network issues
- Explains why certain design choices exist in Redis
- Improves your mental model of how Redis works