Intro
This guide shows the Redis commands operators and developers use every day, with copy-pasteable examples that are safe to try. You will learn how to set and expire keys, manage counters, work with hashes, lists, sets, and sorted sets, iterate safely with SCAN, run simple transactions, use pub/sub, and check instance health. Each example uses a test namespace so you do not pollute real data.
Workflow Overview
Use this sequence to get reliable results:
- Connect and sanity check: PING and INFO.
- Strings and expirations: SET/GET with EX/TTL for cache-like data.
- Counters: INCR/DECR with TTL for rate limits and metrics.
- Collections: HASH, LIST, SET, ZSET commands for structured data.
- Safe iteration: SCAN instead of KEYS.
- Transactions: WATCH, MULTI, EXEC for compare-and-swap updates.
- Pub/Sub: quick fan-out messaging.
- Observability: INFO, MEMORY, SLOWLOG for health and tuning.
Keys and Strings
Connect and ping:
$ redis-cli PING
PONG
Set with expiration and get:
$ redis-cli SET app: test: greeting "hello" EX 60 NX
OK
$ redis-cli GET app: test: greeting
"hello"
$ redis-cli TTL app: test: greeting
(integer) 57
Notes:
- EX sets seconds-to-live. NX avoids overwriting existing keys. Use XX to require existence.
- Use namespaces like app: test:* to isolate samples.
Update value and check existence:
$ redis-cli SET app: test: greeting "hello, world" XX
OK
$ redis-cli EXISTS app: test: greeting
(integer) 1
Get and change TTL in one step:
$ redis-cli GETEX app: test: greeting EX 120
"hello, world"
$ redis-cli TTL app: test: greeting
(integer) 119
Remove expiration if needed:
$ redis-cli PERSIST app: test: greeting
(integer) 1
Delete carefully (prefer targeting test namespaces):
$ redis-cli DEL app: test: greeting
(integer) 1
Multi-key operations:
$ redis-cli MSET app: test: a 1 app: test: b 2
OK
$ redis-cli MGET app: test: a app: test: b
1) "1"
2) "2"
Counters and Rate Limits
Initialize with TTL, then increment atomically:
$ redis-cli SET app: test: login: count 0 EX 3600 NX
OK
$ redis-cli INCR app: test: login: count
(integer) 1
$ redis-cli INCRBY app: test: login: count 5
(integer) 6
$ redis-cli TTL app: test: login: count
(integer) 3597
Pattern: ensure the TTL persists for counters by setting it when the key is first created (SET ... NX EX). For sliding windows consider using INCR with separate timestamped keys per window bucket.
Simple rate limit check (example):
- Increment and get remaining TTL for the window.
$ redis-cli INCR app: test: ratelimit: u123
(integer) 1
$ redis-cli TTL app: test: ratelimit: u123
(integer) -2
- If TTL is -2 (no TTL), apply it now for a 60s window.
$ redis-cli EXPIRE app: test: ratelimit: u123 60
(integer) 1
Hashes
Hashes store object-like fields efficiently.
$ redis-cli HSET app: test: user:1001 name "Ada" plan "pro" visits 1
(integer) 3
$ redis-cli HGET app: test: user:1001 name
"Ada"
$ redis-cli HMGET app: test: user:1001 name plan visits
1) "Ada"
2) "pro"
3) "1"
$ redis-cli HINCRBY app: test: user:1001 visits 1
(integer) 2
Caution: HGETALL can be heavy on very large hashes. Prefer HMGET for specific fields when possible.
Lists
Lists are good for queues and ordered logs.
FIFO queue with LPUSH + RPOP:
$ redis-cli DEL app: test: queue: emails
(integer) 1
$ redis-cli LPUSH app: test: queue: emails id-1 id-2 id-3
(integer) 3
$ redis-cli RPOP app: test: queue: emails
"id-1"
$ redis-cli RPOP app: test: queue: emails
"id-2"
Blocking pop (waits for an item, 5s timeout):
$ redis-cli BRPOP app: test: queue: emails 5
1) "app: test: queue: emails"
2) "id-3"
Tip: Use small payloads (IDs), fetch full data from a hash or database.
Sets and Sorted Sets
Sets store unique items, ideal for membership and de-dup.
$ redis-cli SADD app: test: room: active u1 u2 u2 u3
(integer) 3
$ redis-cli SISMEMBER app: test: room: active u2
(integer) 1
$ redis-cli SMEMBERS app: test: room: active
1) "u1"
2) "u2"
3) "u3"
$ redis-cli SREM app: test: room: active u1
(integer) 1
Sorted sets rank items by score.
$ redis-cli ZADD app: test: scores 100 u1 250 u2 180 u3
(integer) 3
$ redis-cli ZRANGE app: test: scores 0 -1 WITHSCORES
1) "u1"
2) "100"
3) "u3"
4) "180"
5) "u2"
6) "250"
$ redis-cli ZREVRANGE app: test: scores 0 1 WITHSCORES
1) "u2"
2) "250"
3) "u3"
4) "180"
$ redis-cli ZINCRBY app: test: scores 25 u1
"125"
Finding Data Safely
Avoid KEYS in production because it blocks the server on large datasets. Prefer SCAN which is incremental and non-blocking.
Basic SCAN pattern:
$ redis-cli SCAN 0 MATCH app: test:* COUNT 100
1) "cursor"
2) 1) "app: test: a"
2) "app: test: b"
3) "app: test: user:1001"
Loop until the cursor returns 0 to finish scanning.
Collection-specific variants:
- HSCAN key cursor [MATCH pattern] [COUNT n]
- SSCAN key cursor [MATCH pattern] [COUNT n]
- ZSCAN key cursor [MATCH pattern] [COUNT n]
Transactions
Use WATCH + MULTI + EXEC for optimistic locking.
Example: transfer credits if balance is sufficient.
- Seed demo data:
$ redis-cli SET app: test: balance: u1 100
OK
- Start a guarded update:
$ redis-cli WATCH app: test: balance: u1
OK
$ redis-cli GET app: test: balance: u1
"100"
# Suppose you want to debit 30 if balance >= 30
$ redis-cli MULTI
OK
$ redis-cli DECRBY app: test: balance: u1 30
QUEUED
$ redis-cli EXEC
1) (integer) 70
If another client modified the key after WATCH, EXEC returns nil and you should retry the flow.
Pub/Sub Basics
Publish/subscribe is for ephemeral messages (not stored).
Terminal A (subscriber):
$ redis-cli SUBSCRIBE app: test: events
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "app: test: events"
3) (integer) 1
Terminal B (publisher):
$ redis-cli PUBLISH app: test: events "hello subscribers"
(integer) 1
Terminal A output:
1) "message"
2) "app: test: events"
3) "hello subscribers"
Note: Messages are not persisted; late subscribers will not receive past messages.
Observability and Health
Quick checks:
- PING to confirm reachability.
- INFO for server, memory, clients, and keyspace stats.
$ redis-cli INFO server | head -n 5
# Server
redis_version:7.0.x
os:...
Memory usage of a key:
$ redis-cli MEMORY USAGE app: test: user:1001
(integer) 112
Find slow commands:
$ redis-cli SLOWLOG GET 10
1) 1) (integer) 42
2) (integer) 1710000000
3) (integer) 15000
4) 1) "ZRANGE"
2) "big: zset"
3) "0"
4) "1000"
Live traffic debugging:
- MONITOR streams every command. Use carefully and briefly.
$ redis-cli MONITOR
OK
# Press Ctrl-C to quit when finished.
Local Pilot Plan
Keep the first rollout narrow, measurable, and easy to inspect.
Scope:
- Namespace all test keys under app: test:*.
- Exercise only these commands: SET/GET/EXPIRE/TTL, INCR, HSET/HGET, LPUSH/RPOP, SADD/SISMEMBER, ZADD/ZRANGE, SCAN, INFO.
Steps:
- Sanity check: PING and INFO memory; record baseline used_memory and keyspace stats.
- Run the examples in this guide exactly as written.
- Verify outcomes: MGET values, TTLs, SCAN counts, and MEMORY USAGE on a few keys.
- Clean up: DEL only keys under app: test:*.
- Compare before/after INFO keyspace and memory to ensure no strays.
Success criteria:
- All commands return expected values.
- No lingering keys after cleanup (SCAN app: test:* returns none).
- No slowlog entries introduced by the pilot.
Conclusion
You now have a practical toolkit for daily Redis operations: safe key creation with expirations, atomic counters, object-like hashes, queues with lists, unique and ranked collections with sets and sorted sets, non-blocking iteration via SCAN, simple transactions, pub/sub, and essential health checks. Keep examples namespaced, favor SCAN over KEYS, and watch INFO, MEMORY, and SLOWLOG during experiments. Start with the local pilot, confirm results, then expand to your application-specific keys and patterns.