redis-on-mysql
redis-on-mysql is a Redis-compatible proxy that stores all data and Pub/Sub state in MySQL. It speaks RESP2 via resp-async and translates Redis commands into SQL while aiming for Redis 4.0 command family coverage.
What it does
- Stateless proxy: all data lives in MySQL tables.
- Per-user MySQL pools after AUTH; credentials are validated but not stored.
- Redis-style TTL handling and background cleanup.
- Pub/Sub backed by mailbox tables with server-side polling.
Command coverage (high level)
- Connection/basic: PING, QUIT, AUTH, ECHO, SELECT, CLIENT
- Strings/TTL: GET, SET, SETNX, SETEX, PSETEX, INCR, DECR, DEL, EXISTS, EXPIRE, PEXPIRE, EXPIREAT, PEXPIREAT, PERSIST, TTL, PTTL
- Hash, Set, List, Sorted Set families
- Bitmap, Bitfield, HyperLogLog, Geo (string or zset backed)
- Generic keyspace: TYPE, KEYS, SCAN, RANDOMKEY, RENAME, RENAMENX, DBSIZE, FLUSHDB, FLUSHALL, UNLINK
- Cluster compatibility: INFO, NODES, SLOTS, KEYSLOT plus READONLY/READWRITE/ASKING
- Pub/Sub: SUBSCRIBE, UNSUBSCRIBE, PUBLISH
For the exact routing list, see src/handlers/mod.rs.
Quickstart
- Create the schema in MySQL using the migration file:
# option 1: mysql client
# option 2: sqlx cli (requires DATABASE_URL)
- Run the proxy:
# optional
- Connect with redis-cli:
Authentication and tenant routing
- AUTH is required for all data commands.
- Supported formats:
AUTH user passwordAUTH user:password(single argument)
- Tenant id is derived from the username via regex; the no-match policy decides whether to reject, keep the username, or use a literal tenant id.
- The proxy validates credentials by opening a MySQL connection, then reuses a per-user pool for subsequent commands.
Configuration
Configuration can be loaded from a YAML file and/or environment variables. If
REDIS_MYSQL_CONFIG is set, the file is loaded first and any environment
variables override it. Defaults apply when a value is not set anywhere.
Precedence:
- Defaults
- YAML file (if
REDIS_MYSQL_CONFIGis set) - Environment variables
YAML keys are snake_case and use the same units as their env counterparts (milliseconds or seconds in the name).
YAML example
bind_addr: "0.0.0.0:6380"
mysql_addr: "127.0.0.1:3306"
mysql_database: "redis"
mysql_options: ""
auth:
tenant_regex: "^(?P<tenant>[^:]+):"
tenant_no_match: "username"
pool:
max_connections: 16
connect_timeout_ms: 5000
idle_ttl_secs: 600
cleanup:
expire_cleanup_interval_secs: 60
expire_cleanup_batch: 1000
pubsub_cleanup_interval_secs: 60
pubsub_idle_secs: 600
pubsub_message_ttl_secs: 3600
pubsub:
poll_ms: 200
poll_batch: 128
heartbeat_secs: 30
string_set_retry:
max_attempts: 3
backoff_base_ms: 1
backoff_max_ms: 16
string_incr_retry:
max_attempts: 64
backoff_base_ms: 1
backoff_max_ms: 16
deadline_ms: 2000
Enable the file with:
Minimal example
Full example (defaults)
# Core
REDIS_MYSQL_CONFIG=
REDIS_MYSQL_BIND=0.0.0.0:6380
REDIS_MYSQL_ADDR=127.0.0.1:3306
REDIS_MYSQL_DATABASE=redis
REDIS_MYSQL_DSN_OPTIONS=
# Tenant routing
REDIS_MYSQL_TENANT_REGEX="^(?P<tenant>[^:]+):"
REDIS_MYSQL_TENANT_NO_MATCH=username
# MySQL pool
REDIS_MYSQL_POOL_MAX_CONNECTIONS=16
REDIS_MYSQL_POOL_CONNECT_TIMEOUT_MS=5000
REDIS_MYSQL_POOL_IDLE_TTL_SECS=600
# Cleanup cadence
REDIS_MYSQL_EXPIRE_CLEANUP_INTERVAL_SECS=60
REDIS_MYSQL_EXPIRE_CLEANUP_BATCH=1000
REDIS_MYSQL_PUBSUB_CLEANUP_INTERVAL_SECS=60
REDIS_MYSQL_PUBSUB_IDLE_SECS=600
REDIS_MYSQL_PUBSUB_MESSAGE_TTL_SECS=3600
# Pub/Sub polling
REDIS_MYSQL_PUBSUB_POLL_MS=200
REDIS_MYSQL_PUBSUB_POLL_BATCH=128
REDIS_MYSQL_PUBSUB_HEARTBEAT_SECS=30
# Retry/backoff policies
REDIS_MYSQL_SET_RETRY_MAX_ATTEMPTS=3
REDIS_MYSQL_SET_RETRY_BACKOFF_BASE_MS=1
REDIS_MYSQL_SET_RETRY_BACKOFF_MAX_MS=16
REDIS_MYSQL_INCR_RETRY_MAX_ATTEMPTS=64
REDIS_MYSQL_INCR_RETRY_BACKOFF_BASE_MS=1
REDIS_MYSQL_INCR_RETRY_BACKOFF_MAX_MS=16
REDIS_MYSQL_INCR_RETRY_DEADLINE_MS=2000
Optional server limits (override resp-async defaults)
These can also be provided under server: in the YAML file using the same
snake_case names (for example, max_frame_size, read_timeout_ms).
# Request/response limits
REDIS_MYSQL_MAX_FRAME_SIZE=<bytes>
REDIS_MYSQL_MAX_BULK_LEN=<bytes>
REDIS_MYSQL_MAX_ARRAY_LEN=<count>
REDIS_MYSQL_MAX_DEPTH=<count>
REDIS_MYSQL_MAX_INFLIGHT=<count>
REDIS_MYSQL_MAX_CONNECTIONS=<count>
# Timeouts (milliseconds)
REDIS_MYSQL_READ_TIMEOUT_MS=<ms>
REDIS_MYSQL_WRITE_TIMEOUT_MS=<ms>
REDIS_MYSQL_IDLE_TIMEOUT_MS=<ms>
# Queues and IO
REDIS_MYSQL_PUSH_QUEUE_LEN=<count>
REDIS_MYSQL_RESPONSE_QUEUE_LEN=<count>
REDIS_MYSQL_WRITE_BATCH_BYTES=<bytes>
REDIS_MYSQL_TCP_NODELAY=<true|REDIS_MYSQL_BACKLOG=<count>
Non-goals
- Full Redis feature parity (Lua, transactions, modules, streams, ACLs).
- RESP3 support.
- Cluster coordination (single-node cluster responses only).
- TLS termination inside the proxy.