Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Redis Protocol

Redis uses the RESP (Redis Serialization Protocol) for client-server communication. This document explains the protocol format, common commands, and how Xylem supports benchmarking Redis workloads.

What Xylem Supports

Xylem implements RESP and supports:

  • Standard Redis commands (GET, SET, INCR, MGET, WAIT)
  • Custom command templates for any Redis operation
  • Request pipelining for high throughput
  • Configurable key distributions and value sizes

Xylem does not currently support:

  • RESP3 protocol (only RESP2) - TODO: RESP3 adds semantic types (maps, sets, doubles, booleans), push messages, and streaming data. While RESP2 covers all benchmarking needs and works with all Redis versions, RESP3 support could be added for testing RESP3-specific features or comparing protocol performance.
  • Pub/Sub commands
  • Cluster mode (supports single-instance and simple replication)
  • Transactions (MULTI/EXEC)

Protocol Overview

Redis commands are sent as text-based protocol messages. The key characteristics:

Text-based - Human-readable format using printable ASCII characters and CRLF line endings

Binary-safe - Bulk strings can contain any binary data with explicit length encoding

Request-response - Client sends command, server sends response (pipelining allows multiple pending requests)

Stateless - Each command is independent (except for transactions and pub/sub)

RESP Wire Format

RESP (REdis Serialization Protocol) is Redis’s wire protocol. All commands and responses use this format.

RESP Data Types

RESP has five data types, each identified by its first byte:

Simple Strings - Start with +, terminated by \r\n

+OK\r\n

Used for status replies.

Errors - Start with -, terminated by \r\n

-ERR unknown command\r\n

Used for error messages.

Integers - Start with :, terminated by \r\n

:42\r\n

Used for numeric results (INCR, DECR, counter values).

Bulk Strings - Start with $, followed by length and data

$5\r\nhello\r\n
  • $5 - Length in bytes
  • hello - Actual data
  • \r\n - Terminator

Null bulk string (key not found):

$-1\r\n

Arrays - Start with *, followed by element count

*3\r\n$5\r\nhello\r\n$5\r\nworld\r\n:42\r\n
  • *3 - Array with 3 elements
  • Three elements: two bulk strings and one integer

Request Format

All Redis commands are sent as RESP arrays of bulk strings.

GET command:

*2\r\n$3\r\nGET\r\n$8\r\nkey:1234\r\n
  • *2 - Array with 2 elements
  • $3\r\nGET\r\n - Command name (3 bytes)
  • $8\r\nkey:1234\r\n - Key name (8 bytes)

SET command:

*3\r\n$3\r\nSET\r\n$8\r\nkey:1234\r\n$128\r\n<128 bytes>\r\n
  • *3 - Array with 3 elements
  • Command: SET
  • Key: key:1234
  • Value: 128 bytes of data

MGET command (multiple keys):

*11\r\n$4\r\nMGET\r\n$8\r\nkey:1000\r\n$8\r\nkey:1001\r\n...<8 more keys>...\r\n
  • *11 - Command + 10 keys = 11 elements
  • MGET followed by 10 bulk strings

INCR command:

*2\r\n$4\r\nINCR\r\n$7\r\ncounter\r\n

Response Format

Response type depends on the command:

GET (successful):

$128\r\n<128 bytes of data>\r\n

Returns bulk string with value.

GET (key not found):

$-1\r\n

Returns null bulk string.

SET:

+OK\r\n

Returns simple string for success.

INCR:

:43\r\n

Returns integer (new counter value).

MGET:

*3\r\n$5\r\nvalue1\r\n$5\r\nvalue2\r\n$-1\r\n

Returns array with:

  • Two values (bulk strings)
  • One null (missing key)

WAIT:

:2\r\n

Returns integer (number of replicas that acknowledged).

Common Redis Commands

GET key

Retrieves the value of a key.

Request: GET key:1234 Response: Bulk string with value, or null if key doesn’t exist Use case: Read operations, cache lookups

SET key value [options]

Sets a key to hold a string value.

Request: SET key:1234 <data> Response: +OK on success Options: Can include EX/PX for expiration, NX/XX for conditional sets Use case: Write operations, cache updates

INCR key

Increments the integer value of a key by one.

Request: INCR counter:visits Response: Integer (new value after increment) Behavior: Creates key with value 0 if it doesn’t exist, then increments Use case: Counters, rate limiting, analytics

DECR key

Decrements the integer value of a key by one.

Request: DECR counter:stock Response: Integer (new value after decrement) Use case: Inventory tracking, quota management

MGET key [key …]

Returns values of all specified keys.

Request: MGET key:1 key:2 key:3 Response: Array of bulk strings (null for missing keys) Efficiency: Single round-trip for multiple keys Use case: Batch reads, fetching related data

WAIT numreplicas timeout

Blocks until all previous write commands are replicated to at least numreplicas.

Request: WAIT 2 1000 (wait for 2 replicas, timeout 1000ms) Response: Integer (number of replicas that acknowledged) Use case: Testing replication lag, consistency verification Note: Only meaningful in replicated Redis setups

Custom Commands

Redis supports hundreds of commands for different data structures:

Lists: LPUSH, RPUSH, LPOP, RPOP, LRANGE Sets: SADD, SREM, SMEMBERS, SINTER Sorted Sets: ZADD, ZREM, ZRANGE, ZRANGEBYSCORE Hashes: HSET, HGET, HMGET, HGETALL Strings: APPEND, STRLEN, GETRANGE, SETRANGE

Xylem supports these through custom command templates (see Command Selection section below).

Pipelining

Redis supports pipelining: sending multiple commands without waiting for responses.

Benefits:

  • Reduces round-trip latency
  • Increases throughput
  • Efficient use of network bandwidth

How it works:

Client sends:    GET key1    GET key2    GET key3
                 ─────────────────────────────────>
Server responds: <value1>    <value2>    <value3>
                 <─────────────────────────────────

Xylem support: Configure max_pending_per_connection to control pipeline depth.

Benchmarking with Xylem

Xylem allows you to configure various aspects of Redis workloads:

AspectCandidate ChoicesConfig Key
Command Selectionfixed, weightedworkload.operations.strategy
Commandsget, set, incr, mget, wait, customworkload.operations.commands[].name
Key Distributionsequential, random, round-robin, zipfian, gaussianworkload.keys.strategy
Value Sizefixed, uniform, normal, per_commandworkload.value_size.strategy
Pipelining1-N pending requests per connectiontraffic_groups[].max_pending_per_connection
Load Patternconstant, ramp, spike, sinusoidalworkload.pattern.type

Basic Configuration

[target]
protocol = "redis"
address = "127.0.0.1:6379"
transport = "tcp"

Command Selection

Single command:

[workload.operations]
strategy = "fixed"
operation = "get"  # Options: get, set, incr

Mixed workload with weighted distribution:

[workload.operations]
strategy = "weighted"

[[workload.operations.commands]]
name = "get"
weight = 0.7  # 70% reads

[[workload.operations.commands]]
name = "set"
weight = 0.3  # 30% writes

Batch operations (MGET):

[[workload.operations.commands]]
name = "mget"
weight = 0.2
count = 10  # Fetch 10 keys per request

Replication testing (WAIT):

[[workload.operations.commands]]
name = "wait"
weight = 0.1
num_replicas = 2
timeout_ms = 1000

Custom commands with templates:

[[workload.operations.commands]]
name = "custom"
weight = 0.1
template = "ZADD leaderboard __value_size__ player:__key__"

Template variables: __key__, __data__, __value_size__

Key Distribution

Sequential access:

[workload.keys]
strategy = "sequential"
start = 0
max = 100000

Uniform random:

[workload.keys]
strategy = "random"
max = 1000000

Zipfian (hot-key patterns):

[workload.keys]
strategy = "zipfian"
exponent = 0.99
max = 1000000

Gaussian (temporal locality):

[workload.keys]
strategy = "gaussian"
mean_pct = 0.5
std_dev_pct = 0.1
max = 10000

Value Size Distribution

Fixed size:

[workload.value_size]
strategy = "fixed"
size = 128

Uniform distribution:

[workload.value_size]
strategy = "uniform"
min = 64
max = 4096

Normal distribution:

[workload.value_size]
strategy = "normal"
mean = 512.0
std_dev = 128.0
min = 64
max = 4096

Per-command sizes:

[workload.value_size]
strategy = "per_command"
default = { strategy = "fixed", size = 256 }

[workload.value_size.commands.get]
distribution = "fixed"
size = 64

[workload.value_size.commands.set]
distribution = "uniform"
min = 128
max = 1024

Complete Example

[experiment]
name = "redis-realistic-workload"
description = "Mixed operations with hot keys and varied sizes"
duration = "60s"
seed = 42  # Reproducible results

[target]
address = "127.0.0.1:6379"
protocol = "redis"
transport = "tcp"

[workload]
# Hot-key pattern: Zipfian distribution
[workload.keys]
strategy = "zipfian"
exponent = 0.99
max = 1000000
value_size = 512

# 70% reads, 30% writes
[workload.operations]
strategy = "weighted"

[[workload.operations.commands]]
name = "get"
weight = 0.7

[[workload.operations.commands]]
name = "set"
weight = 0.3

# Per-command value sizes
[workload.value_size]
strategy = "per_command"
default = { strategy = "fixed", size = 256 }

[workload.value_size.commands.get]
distribution = "fixed"
size = 64

[workload.value_size.commands.set]
distribution = "normal"
mean = 512.0
std_dev = 128.0
min = 128
max = 2048

[workload.pattern]
type = "constant"
rate = 10000.0

[[traffic_groups]]
name = "redis-benchmark"
threads = [0]
connections_per_thread = 20
max_pending_per_connection = 10  # Pipelining depth

[traffic_groups.policy]
type = "closed-loop"

[traffic_groups.sampling_policy]
type = "limited"
rate = 1.0
max_samples = 100000

[output]
format = "json"
file = "results/redis-benchmark.json"

Common Workload Patterns

Cache workload (CDN, API responses):

  • Commands: 90-95% GET, 5-10% SET
  • Keys: Zipfian (persistent hot content)
  • Sizes: Small reads (64B), larger writes (256-1024B)

Session store:

  • Commands: 70% GET, 30% SET
  • Keys: Gaussian (temporal locality)
  • Sizes: Small lookups (64B), varied session data (128-1024B)

Counter system (rate limiting, analytics):

  • Commands: 50% GET, 50% INCR
  • Keys: Random or uniform
  • Sizes: Small fixed (64B)

Write-heavy (logging, time-series):

  • Commands: 20-30% GET, 70-80% SET
  • Keys: Sequential or temporal
  • Sizes: Uniform or normal distribution

See Also