The fastest way to learn Redis is to type commands, watch the replies, and break things on purpose.

Getting started

Open a terminal, run redis-cli, and confirm the server answers. Use FLUSHDB between sections for a clean slate, but only on a practice instance.

PING # PONG INFO server INFO memory INFO keyspace SELECT 1 # switch to database 1 DBSIZE CONFIG GET maxmemory 

Two shell flags are worth learning on day one: redis-cli -n 2 PING targets a specific database, and redis-cli --raw GET key prints values without quoting or escaping.

Core data types

Most real Redis usage is built from six structures. Run every command, and note each return value: OK, (integer), or nil.

Strings

SET user:1:name "Musa" GET user:1:name SET counter 10 INCR counter # 11 INCRBY counter 5 # 16 APPEND user:1:name " Abu" MSET a 1 b 2 c 3 MGET a b c SET lock:x 1 NX EX 30 # only if absent, 30s TTL SET k v XX # only if present GETEX k EX 60 

Keys and expiry

EXISTS user:1:name TYPE user:1:name EXPIRE user:1:name 60 TTL user:1:name PERSIST user:1:name UNLINK k # asynchronous delete SCAN 0 MATCH user:* COUNT 100 OBJECT ENCODING counter # int, embstr or raw 

Compare KEYS * with SCAN, and never use KEYS in production: it blocks the server while it walks the whole keyspace. Then set a 5-second TTL and run TTL repeatedly to watch the key expire.

Hashes

HSET user:1 name Musa age 30 city Dhaka HGET user:1 name HMGET user:1 name city HGETALL user:1 HINCRBY user:1 age 1 HEXISTS user:1 city HDEL user:1 city HSCAN user:1 0 

Lists

RPUSH queue job1 job2 job3 LPUSH queue job0 LRANGE queue 0 -1 LPOP queue LTRIM queue 0 99 BLPOP queue 10 # blocking pop; try from a second terminal LMOVE queue processing LEFT RIGHT 

For a reliable queue, move each job with LMOVE queue processing, process it, then remove it with LREM processing 1 job.

Sets

SADD tags:post1 redis db cache SADD tags:post2 redis golang SISMEMBER tags:post1 redis SINTER tags:post1 tags:post2 SUNION tags:post1 tags:post2 SDIFF tags:post1 tags:post2 SPOP tags:post1 

Sorted sets

ZADD leaderboard 100 alice 250 bob 175 carol ZRANGE leaderboard 0 -1 WITHSCORES ZRANGE leaderboard 0 2 REV WITHSCORES # top 3 (Redis 6.2+) ZRANK leaderboard alice ZINCRBY leaderboard 50 alice ZRANGEBYSCORE leaderboard 100 200 ZREM leaderboard carol 

Sorted sets are the most versatile structure on this list: leaderboards, priority queues, time-ordered indexes and rate limiters all reduce to them.

Beyond the basics

These structures trade generality for extreme efficiency, and each solves one problem very well.

Bitmaps, HyperLogLog and geo

Bitmaps track yes/no flags per user ID in a few bytes each. HyperLogLog counts unique items approximately in about 12 KB, however many there are. Geo commands store coordinates and answer distance and radius queries.

SETBIT login:2026-10-12 1001 1 GETBIT login:2026-10-12 1001 BITCOUNT login:2026-10-12 BITOP AND active:both login:day1 login:day2 PFADD visitors:today u1 u2 u3 u1 PFCOUNT visitors:today # 3 PFMERGE visitors:week visitors:d1 visitors:d2 GEOADD shops 90.4125 23.8103 "dhaka-a" 91.8123 22.3569 "ctg-a" GEODIST shops dhaka-a ctg-a km GEOSEARCH shops FROMLONLAT 90.4 23.8 BYRADIUS 50 km ASC 

Streams

Streams are an append-only log with consumer groups, which makes them the durable alternative to Pub/Sub and lists for event processing.

XADD orders * user 1 amount 500 XLEN orders XRANGE orders - + XREAD BLOCK 5000 STREAMS orders $ XGROUP CREATE orders workers $ MKSTREAM XREADGROUP GROUP workers w1 COUNT 1 STREAMS orders > XACK orders workers <id> XPENDING orders workers XCLAIM orders workers w2 60000 <id> XTRIM orders MAXLEN ~ 1000 

Use two terminals: one producer, one consumer-group worker. Kill the worker before it runs XACK, then recover the unacknowledged message with XPENDING and XCLAIM. That exercise teaches more about delivery guarantees than any documentation page.

Messaging and atomicity

Redis is single-threaded for command execution, so a single command is always atomic. The tools below extend that guarantee to groups of commands.

Pub/Sub

In terminal A, subscribe. In terminal B, publish.

# Terminal A SUBSCRIBE news PSUBSCRIBE news:* # Terminal B PUBLISH news "hello" PUBSUB CHANNELS PUBSUB NUMSUB news 

Pub/Sub is fire-and-forget: a subscriber that connects late misses earlier messages. Confirm this yourself, then contrast it with Streams, which keep history.

Transactions

MULTI SET balance 100 DECRBY balance 30 EXEC WATCH balance # optimistic locking MULTI DECRBY balance 10 EXEC # nil if another client changed balance 

Trigger a WATCH failure by modifying balance from a second terminal before EXEC. Also note that Redis transactions have no rollback: if one command fails at runtime, the others still run.

Lua scripting

A Lua script runs as one atomic unit, which is the right tool for read-modify-write logic that WATCH makes clumsy.

EVAL "return redis.call('GET', KEYS[1])" 1 user:1:name # Safe unlock: delete only if the token matches EVAL "if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 lock:x token123 SCRIPT LOAD "return 'hi'" EVALSHA <sha> 0 

Running Redis

Knowing the commands is half the job. The other half is knowing how Redis behaves when memory fills, a process restarts, or a client misbehaves.

Persistence

CONFIG GET save BGSAVE LASTSAVE CONFIG SET appendonly yes BGREWRITEAOF INFO persistence 

Set a key, run SHUTDOWN SAVE, restart with sudo systemctl restart redis-server, and check that the key survived. Then repeat with persistence disabled and watch the data disappear.

Memory and eviction

CONFIG SET maxmemory 10mb CONFIG SET maxmemory-policy allkeys-lru MEMORY USAGE user:1 INFO stats # evicted_keys, keyspace_hits, keyspace_misses 
redis-cli --bigkeys redis-cli --memkeys 

Fill memory with a loop and watch evicted_keys climb. Try noeviction, volatile-lru and allkeys-lfu to see how each policy changes which keys survive.

Monitoring and debugging

MONITOR # live command stream; development only SLOWLOG GET 10 CONFIG SET slowlog-log-slower-than 1000 CLIENT LIST LATENCY DOCTOR 
redis-cli --latency redis-cli --stat redis-benchmark -q -n 100000 -c 50 

Security and ACLs

CONFIG SET requirepass strongpass AUTH strongpass ACL SETUSER reader on >readpass ~cache:* +get +mget ACL LIST AUTH reader readpass 

Log in as reader and confirm that SET fails with NOPERM. Least-privilege users are one of the cheapest hardening steps Redis offers.

Replication on one machine

Copy the config, change the port, data directory and pidfile, then start a replica:

sudo cp /etc/redis/redis.conf /etc/redis/redis-6380.conf redis-server /etc/redis/redis-6380.conf --replicaof 127.0.0.1 6379 
INFO replication WAIT 1 100 REPLICAOF NO ONE # promote the replica 

Write on the primary and read on the replica. Then try writing to the replica to see the read-only error.

Real-world patterns

Once the commands feel natural, combine them into the patterns production systems actually use.

Pattern Commands
Cache-aside GET product:5; on a miss, SET product:5 <json> EX 300
Fixed-window rate limiter INCR rate:user1:202610121530, then EXPIRE it to 60 seconds; reject above the limit
Distributed lock SET lock:order:9 <token> NX PX 10000; release with the Lua compare-and-delete script
Leaderboard ZINCRBY to score, ZRANGE ... REV to read the top N
Session store Hash session:<id>, with EXPIRE refreshed on each request
Unique visitors PFADD and PFCOUNT
Daily active users One bitmap per date, combined with BITOP
Job queue List with BLPOP, or Streams with consumer groups
Idempotency key SET idem:<key> 1 NX EX 86400

The distributed lock deserves special care. Always store a unique token as the value and release with the Lua script, so one client can never delete a lock another client now holds.

Challenges

Try these without looking anything up. Each one forces you to combine commands you have already met.

  1. Build a sliding-window rate limiter with a sorted set, using ZADD, ZREMRANGEBYSCORE and ZCARD.
  2. Keep a "recently viewed, last 10 items" list per user with LPUSH and LTRIM.
  3. Implement a delayed job queue with a sorted set scored by run timestamp.
  4. Build autocomplete with ZRANGEBYLEX.
  5. Write a Lua script that decrements stock atomically and refuses to go below zero.
  6. Compare the memory used by 10,000 small keys against one hash with 10,000 fields, using MEMORY USAGE.

The last exercise tends to surprise people: small hashes use a compact encoding, so the single hash is often far cheaper.

Conclusion

Redis rewards hands-on practice more than reading. Work through the sections in order, break things deliberately (kill a worker mid-stream, fill memory, fail a WATCH), and finish with the challenges.