Redis Interview Questions
How to use: Click any question to expand the answer.
🟢 Easy (Q1–Q50)
Section titled “🟢 Easy (Q1–Q50)”Q1. What is Redis? Easy
Redis (Remote Dictionary Server) is an open-source, in-memory data structure store used as a database, cache, message broker, and streaming engine. It was created by Salvatore Sanfilippo in 2009.
Key characteristics:
- All data is stored in RAM for extreme speed (sub-millisecond latency)
- Supports rich data types: Strings, Lists, Sets, Sorted Sets, Hashes, Streams, Bitmaps, HyperLogLog, Geospatial
- Single-threaded event loop for atomic operations
- Built-in replication, persistence, clustering, and high availability
redis-cli SET hello "world"# OKredis-cli GET hello# "world"Q2. What makes Redis different from traditional databases like MySQL or PostgreSQL? Easy
| Feature | Redis | Traditional RDBMS (MySQL/PostgreSQL) |
|---|---|---|
| Storage | Primarily in-memory (RAM) | Primarily disk-based |
| Speed | Microsecond latency | Millisecond latency |
| Data Model | Key-value with rich data structures | Tables, rows, columns, relationships |
| Queries | By key, or simple patterns | Complex SQL (JOINs, subqueries, aggregations) |
| Persistence | Optional (RDB/AOF) | Default (every write goes to disk) |
| Use Case | Caching, real-time, session store | Primary data store, complex queries |
In short: Redis is 100-1000x faster than traditional databases but with less query flexibility and optional durability.
Q3. What data types does Redis support? Easy
Redis supports the following core data types:
| Data Type | Description | Example Command |
|---|---|---|
| String | Text, numbers, binary (up to 512MB) | SET, GET, INCR |
| List | Ordered collection of strings (linked list) | LPUSH, RPUSH, LRANGE |
| Set | Unordered unique strings | SADD, SMEMBERS, SINTER |
| Sorted Set | Unique strings with numeric scores (ordered by score) | ZADD, ZRANGE, ZRANK |
| Hash | Map of field-value pairs | HSET, HGET, HGETALL |
| Stream | Append-only log of entries | XADD, XREAD, XRANGE |
| Bitmap | Bit-level operations on strings | SETBIT, GETBIT, BITCOUNT |
| HyperLogLog | Probabilistic cardinality estimator | PFADD, PFCOUNT |
| Geospatial | Latitude/longitude data | GEOADD, GEODIST, GEORADIUS |
Q4. What is the maximum size of a Redis String value? Easy
A Redis String value can be up to 512 MB. This applies to String values, but also to the string elements within Lists, Sets, and other data structures. Keys can also be up to 512 MB, although practical keys are much shorter.
Q5. How does Redis achieve high performance despite being single-threaded? Easy
Redis uses a non-blocking, event-driven architecture with I/O multiplexing:
- The main thread runs an event loop that processes client connections one at a time
- I/O multiplexing (epoll on Linux, kqueue on macOS) allows a single thread to handle thousands of concurrent connections efficiently
- Since Redis operations are in-memory and execute in microseconds, there’s minimal blocking
- No lock contention or context switching overhead (unlike multi-threaded databases)
Note: As of Redis 6, some I/O operations (socket reads/writes, eviction) are handled by background threads, but command execution remains single-threaded.
Q6. What is the difference between `SET` and `SETNX`? Easy
SET key value— sets the key regardless of whether it already exists (overwrites)SETNX key value— Set if Not Exists. Only sets the key if it does NOT already exist
redis> SETNX mykey "hello"(integer) 1 # Key was set successfully
redis> SETNX mykey "world"(integer) 0 # Key already exists, not set
redis> GET mykey"hello"SETNX is commonly used for implementing distributed locks.
Q7. What is TTL in Redis and how do you set it? Easy
TTL (Time To Live) is the number of seconds a key will remain in Redis before being automatically deleted.
# Set a key with TTL of 60 secondsSET session:123 "user_data"EXPIRE session:123 60
# Or set TTL in one commandSETEX session:123 60 "user_data"
# Check remaining TTLTTL session:123 # Returns seconds remaining, -1 means no TTL, -2 means key doesn't exist
# Set TTL in millisecondsPEXPIRE session:123 60000TTL is critical for cache management — it prevents stale data from accumulating in memory.
Q8. What is the difference between Redis Lists and Redis Sets? Easy
| Feature | List | Set |
|---|---|---|
| Order | Preserves insertion order | No order (unordered) |
| Duplicates | Allows duplicates | Unique elements only |
| Operations | LPUSH/RPUSH, LPOP/RPOP, LRANGE | SADD, SREM, SMEMBERS, SINTER, SUNION |
| Underlying | Linked list | Hash table |
| Use Case | Queue, stack, message buffer | Tags, unique visitors, set operations |
# List - ordered, allows duplicatesLPUSH mylist "a" "b" "c" # ["c", "b", "a"]
# Set - unordered, uniqueSADD myset "a" "b" "c" # All three addedSADD myset "a" # Not added (already exists)Q9. How do you increment a value atomically in Redis? Easy
Use the INCR command to atomically increment a numeric string value by 1:
redis> SET counter 10OKredis> INCR counter(integer) 11redis> INCR counter(integer) 12
# Increment by a specific amountINCRBY counter 5 # Returns 17
# DecrementDECR counter # Returns 16DECRBY counter 3 # Returns 13INCR is atomic — even with thousands of concurrent clients, each increment happens without race conditions.
Q10. What is the `LRANGE` command used for? Easy
LRANGE key start stop returns a range of elements from a List. Both start and stop are zero-based indexes.
redis> RPUSH mylist "a" "b" "c" "d" "e"redis> LRANGE mylist 0 -1 # All elements → ["a", "b", "c", "d", "e"]redis> LRANGE mylist 0 2 # First 3 elements → ["a", "b", "c"]redis> LRANGE mylist -3 -1 # Last 3 elements → ["c", "d", "e"]Negative indexes count from the end of the list (-1 is the last element).
Q11. What is `HSET` and `HGET` in Redis? Easy
Redis Hashes are field-value pairs stored under a key — similar to a JavaScript object or a Python dictionary.
# Set multiple fieldsHSET user:1001 name "Alice" age 30 city "NYC"
# Get a single fieldHGET user:1001 name # "Alice"
# Get all fields and valuesHGETALL user:1001 # 1) "name" 2) "Alice" 3) "age" 4) "30" 5) "city" 6) "NYC"
# Get multiple fieldsHMGET user:1001 name age # 1) "Alice" 2) "30"
# Increment a numeric fieldHINCRBY user:1001 age 1 # Returns 31Hashes are ideal for storing objects — they’re memory-efficient for grouped data.
Q12. How do you check if a key exists in Redis? Easy
Use the EXISTS command:
redis> SET mykey "hello"OKredis> EXISTS mykey(integer) 1 # Key existsredis> EXISTS nonexistent(integer) 0 # Key doesn't existEXISTS can check multiple keys at once, returning the count of existing keys.
You can also check the type of a key with TYPE:
redis> TYPE mykey"string"Q13. What is `SINTER` used for? Easy
SINTER returns the set intersection — elements common to all specified Sets:
redis> SADD set1 "a" "b" "c" "d"redis> SADD set2 "c" "d" "e" "f"
redis> SINTER set1 set21) "c"2) "d"Related commands:
SUNION— union of sets (all unique elements from any set)SDIFF— difference (elements in first set not in others)SINTERSTORE— store the intersection into a new set
Set operations like these are useful for tags, permissions, and recommendation systems.
Q14. What is a Sorted Set and how is it different from a regular Set? Easy
A Sorted Set (ZSET) is like a Set where each member has a numeric score. Members are automatically sorted by score.
| Feature | Set | Sorted Set |
|---|---|---|
| Order | None | By score (ascending) |
| Uniqueness | Yes | Yes |
| Score | No | Each member has a float score |
| Operations | SADD, SMEMBERS, SINTER | ZADD, ZRANGE, ZRANK, ZSCORE |
# Adding members with scoresZADD leaderboard 100 "Alice" 85 "Bob" 200 "Charlie" 95 "Diana"
# Get top 3 (highest scores)ZREVRANGE leaderboard 0 2 WITHSCORES1) "Charlie" 2) "200" 3) "Alice" 4) "100" 5) "Diana" 6) "95"
# Get Alice's rank (0-based)ZREVRANK leaderboard "Alice" # Returns 1 (2nd place)Sorted Sets are used for leaderboards, priority queues, and rate limiters.
Q15. How do you delete keys in Redis? Easy
Use the DEL command:
# Delete a single keyDEL mykey(integer) 1 # 1 key was deleted
# Delete multiple keysDEL key1 key2 key3(integer) 3
# Delete keys matching a pattern (not built-in, but via SCAN + DEL)# Use with caution on large datasetsTo delete all keys in the database:
# Delete all keys in current databaseFLUSHDB
# Delete all keys in all databasesFLUSHALLWarning:
FLUSHALLandFLUSHDBare destructive and synchronous by default. UseFLUSHALL ASYNCin Redis 6+.
Q16. What is Redis used for? List common use cases. Easy
Common Redis use cases:
- Caching — Cache database query results, API responses, rendered pages
- Session Storage — Store user sessions (most popular use case)
- Real-time Leaderboards — Sorted Sets for gaming or engagement scores
- Rate Limiting — INCR + EXPIRE to limit API requests
- Message Queue — Lists with LPUSH/RPOP or Streams
- Pub/Sub Messaging — Real-time notifications, chat
- Distributed Locks — SETNX or Redlock algorithm
- Counters — INCR for page views, likes, shares
- Geolocation — GEOADD / GEORADIUS for location-based features
- Session/Token Blacklisting — Quick O(1) lookups with TTL auto-cleanup
Q17. What are Redis keys? Any rules or conventions? Easy
Redis keys are binary-safe strings (up to 512MB). Best practices:
- Use meaningful namespaces —
object:id:field(e.g.,user:1001:name) - Use colons as separators — this creates a hierarchical structure
- Keep keys short but readable —
u:1001:nis too cryptic - Consistent naming — singular
user:1001not mixingusers:1001 - Max length — aim for under 100 bytes (longer keys use more memory)
Good key examples:
user:1001:profilearticle:42:commentssession:abc123xyzorder:2024:03:15:7890Q18. What is `RENAME` in Redis? Easy
RENAME key newkey renames an existing key to a new key name:
redis> SET mykey "hello"OKredis> RENAME mykey mynewkeyOKredis> GET mykey(nil)redis> GET mynewkey"hello"If newkey already exists, it’s overwritten automatically. Use RENAMENX to only rename if the new key doesn’t exist.
Q19. What is `EXPIRE` and `PERSIST`? Easy
EXPIRE key seconds— Set a TTL (timeout) on an existing keyPERSIST key— Remove the TTL, making the key persistent
redis> SET temp "data"OKredis> EXPIRE temp 3600 # Expires in 1 hour(integer) 1
redis> TTL temp # Check remaining time(integer) 3598
redis> PERSIST temp # Remove TTL(integer) 1
redis> TTL temp(integer) -1 # No TTL (persistent)You can also set TTL at key creation using SETEX key seconds value.
Q20. How do you find all keys matching a pattern? Easy
Use the KEYS pattern command or the preferred SCAN command:
# KEYS — synchronous, dangerous in production with many keysKEYS user:* # All keys starting with "user:"KEYS * # ALL keys (DON'T DO THIS IN PRODUCTION)
# SCAN — cursor-based, safe for production (non-blocking)SCAN 0 MATCH user:* COUNT 100# Returns: cursor + array of matching keysNever use
KEYSin production — it blocks Redis for all other operations until complete. Always useSCANinstead.
Q21. What is `RPUSH` vs `LPUSH`? Easy
Both push elements into a Redis List:
RPUSH key value [value ...]— Pushes to the right (tail) of the listLPUSH key value [value ...]— Pushes to the left (head) of the list
RPUSH mylist "a" "b" # ["a", "b"]LPUSH mylist "z" # ["z", "a", "b"]Use patterns:
RPUSH+LPOP= FIFO Queue (first in, first out)LPUSH+LPOP= LIFO Stack (last in, first out)
Q22. What is `ZRANK` and `ZREVRANK`? Easy
Both get the position (rank) of a member in a Sorted Set:
ZRANK key member— Returns the 0-based rank of the member (lowest score = rank 0)ZREVRANK key member— Returns rank in reverse order (highest score = rank 0)
ZADD scores 50 "Alice" 80 "Bob" 30 "Charlie"
ZRANK scores "Alice" # 1 (second lowest score)ZRANK scores "Charlie" # 0 (lowest score)ZREVRANK scores "Bob" # 0 (highest score)ZREVRANK scores "Charlie" # 2 (lowest score)Q23. What is the `INFO` command in Redis? Easy
INFO returns detailed information and statistics about the Redis server:
redis> INFO
# Sections include:# Server — version, OS, uptime, process_id# Clients — connected_clients, blocked_clients# Memory — used_memory, peak_memory, fragmentation_ratio# Persistence — rdb_last_save_time, aof_enabled# Stats — total_connections_received, commands_processed# Replication — role (master/slave), connected_slaves# Keyspace — db0:keys=1500,expires=200Get specific sections: INFO memory, INFO stats, INFO keyspace, etc.
Q24. What is the default port for Redis? Easy
The default port for Redis is 6379. TLS connections typically use port 6380.
6380 is also the default port for Redis Sentinel to communicate. Redis Cluster uses port 6379 + 10000 (16379) for cluster bus communication.
Q25. How do you connect to a Redis server? Easy
Using the redis-cli command-line tool:
# Connect to localhost:6379 (default)redis-cli
# Connect to a specific host and portredis-cli -h myredis.example.com -p 6379
# Connect with authenticationredis-cli -h myredis.example.com -a mypassword
# Connect to a specific database (0-15)redis-cli -n 5
# Test the connectionredis-cli ping# PONGMost Redis clients (Node.js, Python, Java, etc.) follow similar connection patterns with host, port, password, and database parameters.
Q26. What is `SELECT` in Redis? Easy
SELECT index switches the current database connection to a different database index.
# Redis has 16 databases by default (index 0-15)SELECT 0 # Switch to database 0 (default)SELECT 5 # Switch to database 5Each database has its own keyspace — keys in DB 0 are isolated from DB 1.
Best Practice: Modern Redis usage often avoids multiple databases. Using key namespacing (e.g.,
cache:user:1001) is more scalable and works transparently with Redis Cluster.
Q27. What is `DBSIZE` in Redis? Easy
DBSIZE returns the number of keys in the currently selected database:
redis> SELECT 0OKredis> DBSIZE(integer) 1500 # 1500 keys in database 0Note that DBSIZE only counts keys, not the size of individual values. Use MEMORY USAGE key to check how much memory a specific key uses.
Q28. What is the purpose of Redis Pub/Sub? Easy
Redis Pub/Sub is a publish-subscribe messaging pattern where:
- Publishers send messages to channels (don’t know who receives)
- Subscribers listen to channels (don’t know who sends)
- Messages are fire-and-forget — no persistence, no replay
# Terminal 1 — SubscribeSUBSCRIBE notifications
# Terminal 2 — PublishPUBLISH notifications "Hello subscribers!"
# Terminal 1 receives:# 1) "message"# 2) "notifications"# 3) "Hello subscribers!"Use cases: real-time notifications, live chat, presence updates, broadcast events.
Q29. What does `FLUSHALL` do? Easy
FLUSHALL deletes all keys in all databases on the Redis server.
# Synchronous (blocks until complete)FLUSHALL
# Asynchronous (non-blocking, Redis 6+)FLUSHALL ASYNCFLUSHDB deletes all keys in the current database only.
Warning: These are destructive operations. Use with extreme caution in production. Always prefer
FLUSHALL ASYNCto avoid blocking.
Q30. What is the `TYPE` command? Easy
TYPE key returns the data type of the value stored at a given key:
SET mystring "hello" → TYPE mystring → "string"LPUSH mylist "a" → TYPE mylist → "list"SADD myset "a" → TYPE myset → "set"ZADD myzset 1 "a" → TYPE myzset → "zset"HSET myhash field "value" → TYPE myhash → "hash"XADD mystream * field "value" → TYPE mystream → "stream"Returns "none" if the key doesn’t exist.
Q31. What is the difference between `GET` and `MGET`? Easy
GET key— returns the value of a single keyMGET key [key ...]— returns values of multiple keys in one round trip
SET user:1 "Alice"SET user:2 "Bob"SET user:3 "Charlie"
GET user:1 # "Alice" (1 round trip)MGET user:1 user:2 user:3 # ["Alice", "Bob", "Charlie"] (1 round trip)Always prefer MGET over multiple GET calls when retrieving several keys — it reduces network round trips significantly.
Similarly, MSET sets multiple keys atomically.
Q32. What is `LPOP` and `RPOP`? Easy
Both remove and return elements from a List:
LPOP key [count]— Removes and returns the left (head) elementRPOP key [count]— Removes and returns the right (tail) element
RPUSH queue "task1" "task2" "task3" # ["task1", "task2", "task3"]LPOP queue # "task1" (queue → ["task2", "task3"])LPOP queue # "task2"LPOP queue # "task3"Common patterns:
RPUSH+LPOP= FIFO QueueLPUSH+LPOP= LIFO StackBRPOP/BLPOP= Blocking versions (wait for elements if list is empty)
Q33. How do you set multiple key-value pairs at once? Easy
Use MSET (Multi Set):
MSET key1 "value1" key2 "value2" key3 "value3"This is atomic — all keys are set or none are. It uses a single round trip for better performance.
Similarly, MGET retrieves multiple values:
MGET key1 key2 key3 # Returns ["value1", "value2", "value3"]Q34. What are blocking list operations in Redis? Easy
Blocking list operations wait for an element to be available instead of returning nil:
BLPOP key [key ...] timeout— Blocking LPOP (waits up totimeoutseconds)BRPOP key [key ...] timeout— Blocking RPOPBRPOPLPUSH source destination timeout— Blocking RPOPLPUSH
# Terminal 1 (worker) — blocks until an element is availableBRPOP taskqueue 0 # 0 = wait indefinitely
# Terminal 2 (producer) — publishes a taskRPUSH taskqueue "process_order_123"
# Terminal 1 immediately returns: ["taskqueue", "process_order_123"]Blocking operations are useful for implementing reliable work queues — workers wait for tasks without busy polling.
Q35. What is `LLEN`? Easy
LLEN returns the length (number of elements) of a List:
RPUSH mylist "a" "b" "c" "d" "e"LLEN mylist # (integer) 5Other similar commands:
SCARD— cardinality of a SetZCARD— cardinality of a Sorted SetHLEN— number of fields in a HashSTRLEN— length (bytes) of a String value
Q36. How do you get all members of a Set? Easy
Use SMEMBERS key to return all members of a Set:
SADD tags:article:42 "redis" "database" "caching"SMEMBERS tags:article:421) "redis"2) "database"3) "caching"For Sorted Sets, use ZRANGE key 0 -1 to get all members. For large sets, prefer SSCAN / ZSCAN instead of SMEMBERS / ZRANGE to avoid blocking.
Q37. What is `SCARD` in Redis? Easy
SCARD key returns the cardinality (number of members) of a Set:
SADD myset "a" "b" "c" "a" # "a" already exists, only 3 unique addedSCARD myset # (integer) 3ZCARD for Sorted Sets and HLEN for Hashes work similarly.
Q38. What is the difference between `SET` with EX and `SETEX`? Easy
Both set a key with a TTL, but:
# SET with EX option (Redis 2.6.12+)SET mykey "hello" EX 60
# SETEX (dedicated command)SETEX mykey 60 "hello"The difference:
SET ... EXsupports additional options likeNX(only set if not exists) andGET(return old value)SET ... EXis preferred for distributed locks and atomic operationsSETEXis simpler but less flexible
Both are atomic and preferred over SET + EXPIRE (two separate commands that could fail between).
Q39. What is `ZCOUNT` used for? Easy
ZCOUNT key min max returns the number of members in a Sorted Set with scores within the given range:
ZADD prices 10 "apple" 20 "banana" 30 "cherry" 40 "date" 50 "elderberry"
ZCOUNT prices 20 40 # Returns 3 (banana, cherry, date)ZCOUNT prices -inf 30 # Returns 3 (apple, banana, cherry)This is useful for analytics — e.g., counting orders within a price range.
Q40. What is the `RANDOMKEY` command? Easy
RANDOMKEY returns a random key from the current database:
redis> RANDOMKEY"user:42"redis> RANDOMKEY"session:abc123"It returns nil if the database is empty. Useful for sampling data or debugging.
Q41. What are the namespace/database limits in Redis? Easy
- Max databases: 16 by default (configurable via
databasessetting) - Max keys per database: ~2³² (4.2 billion), theoretically limited by available RAM
- Max key size: 512 MB
- Max value size: 512 MB (for Strings; Lists can hold up to ~4.2 billion elements)
- Max number of clients: ~10,000 (depends on OS limits and file descriptors)
Q42. What is `BITCOUNT` and `GETBIT`? Easy
Redis Bitmaps are operations on String values at the bit level:
SETBIT key offset value— Set a bit at the given offset (0 or 1)GETBIT key offset— Get the bit value at the given offsetBITCOUNT key [start end]— Count number of bits set to 1BITOP operation destkey key [key ...]— Perform bitwise AND/OR/XOR/NOT
# Track daily user sign-ins (one bit per day)SETBIT user:1001:signin 0 1 # Day 0: signed inSETBIT user:1001:signin 1 1 # Day 1: signed inSETBIT user:1001:signin 3 1 # Day 3: signed in (missed day 2)
BITCOUNT user:1001:signin # Returns 3 (signed in 3 days)GETBIT user:1001:signin 2 # 0 (didn't sign in day 2)Bitmaps are extremely memory efficient — 1 million bits only uses ~125KB.
Q43. What is the `TTL` command and what do its return values mean? Easy
TTL key returns the remaining time-to-live (in seconds) of a key:
| Return Value | Meaning |
|---|---|
N > 0 | Key exists and will expire in N seconds |
-1 | Key exists but has no TTL (persistent) |
-2 | Key does not exist (or has already expired) |
SETEX mykey 60 "hello"TTL mykey # ~58 (seconds remaining)PERSIST mykeyTTL mykey # -1 (persistent, no TTL)DEL mykeyTTL mykey # -2 (key doesn't exist)PTTL returns the remaining time in milliseconds.
Q44. What is the `SISMEMBER` command? Easy
SISMEMBER key member checks whether a value is a member of a Set:
SADD admins "alice" "bob"SISMEMBER admins "alice" # (integer) 1 — is a memberSISMEMBER admins "eve" # (integer) 0 — not a memberReturns 1 (member) or 0 (not a member). O(1) time complexity.
For Sorted Sets, use ZSCORE key member — returns the score if the member exists, or nil if not.
Q45. What is `HGETALL` and when should you avoid it? Easy
HGETALL key returns all fields and values of a Hash:
HSET user:42 name "Alice" email "alice@example.com" age 30HGETALL user:421) "name" 2) "Alice"3) "email" 4) "alice@example.com"5) "age" 6) "30"When to avoid HGETALL:
- For large hashes (hundreds of fields) — it blocks Redis and uses significant memory
- Instead, use
HSCANfor iterating large hashes, or useHMGETto get specific fields
Q46. What is `STRLEN`? Easy
STRLEN key returns the length (in bytes) of a String value:
SET mykey "hello"STRLEN mykey # (integer) 5
SET unicode "héllo"STRLEN unicode # (integer) 6 (é is 2 bytes in UTF-8)For Hashes, use HSTRLEN key field. For Lists, use LLEN. For Sets, use SCARD.
Q47. What is `APPEND` in Redis? Easy
APPEND key value appends a value to the end of an existing string. If the key doesn’t exist, it creates a new key (like SET):
SET mykey "Hello"APPEND mykey " World!"GET mykey # "Hello World!"
# APPEND returns the new length of the stringAPPEND mykey " Again" # Returns 19Useful for building strings incrementally, like accumulating log entries.
Q48. What is `GETSET`? Easy
GETSET key value atomically sets a key to a new value and returns the old value:
SET counter "10"GETSET counter "20" # Returns "10" (the old value)GET counter # Returns "20" (the new value)This is useful for atomic counter resets — you can read the old value and reset in one operation, without race conditions.
If the key doesn’t exist, it returns nil and creates the key.
Q49. How do you check the memory usage of a key? Easy
Use MEMORY USAGE key [SAMPLES count]:
SET mykey "Hello, World!"MEMORY USAGE mykey # Returns bytes used (e.g., 64)
HSET user:1001 name "Alice" bio "..." city "NYC"MEMORY USAGE user:1001 # Returns bytes including overheadThis tells you the actual memory consumed by the key, including Redis data structure overhead (not just the raw value size). The SAMPLES option is useful for large containers (Lists, Sets, Hashes) to estimate memory usage.
Q50. What is the `PING` command used for? Easy
PING tests whether the Redis server is alive and responsive:
redis> PINGPONG
# With a custom messageredis> PING "hello""hello"Common uses:
- Health checks — monitoring tools ping Redis to check availability
- Connection testing — verify the client can reach the server
- Latency measurement — measure round-trip time
A successful PONG does NOT guarantee the server is functioning correctly — it just means the event loop is running. Use INFO for deeper health checks.
🟡 Medium (Q51–Q110)
Section titled “🟡 Medium (Q51–Q110)”Q51. What is Redis Persistence and what options are available? Medium
Redis offers two persistence mechanisms:
RDB (Redis Database File)
- Creates point-in-time binary snapshots of the dataset
- Compact, fast to restore
- Can lose data between snapshots (configurable interval)
- File:
dump.rdb
AOF (Append-Only File)
- Logs every write operation to an append-only file
- More durable (configurable fsync: every second, always, or never)
- Larger file size, slower to replay on restart
- File:
appendonly.aof
Best Practice: Enable both RDB and AOF for data safety and fast restarts.
Redis 4.0+: AOF rewrite + RDB hybrid — uses RDB as base with incremental AOF on top.
# Configurationsave 900 1 # RDB: save if at least 1 key changed in 900 secondssave 300 10 # RDB: save if at least 10 keys changed in 300 secondsappendonly yes # Enable AOFappendfsync everysec # AOF fsync: compromise between safety and speedQ52. How do Redis Transactions work (MULTI/EXEC)? Medium
Redis Transactions use MULTI, EXEC, DISCARD, and WATCH:
MULTI— Marks the start of a transaction block- Commands — Queued, not executed immediately
EXEC— Executes all queued commands atomically (no other commands interleaved)DISCARD— Flushes all queued commands
MULTISET account:1001:balance 100INCRBY account:1002:balance 50EXECKey characteristics:
- Redis transactions are all-or-nothing (if EXEC fails, nothing runs)
- But they are NOT rollback — if a command fails during execution, others still run
- Transactions are atomic — no other client can execute commands between MULTI and EXEC
Q53. What is the WATCH command and how does it implement optimistic locking? Medium
WATCH implements optimistic locking for Redis transactions. You watch one or more keys before MULTI:
WATCH account:1001:balancebalance = GET account:1001:balance # Client-side read (pseudo-code)new_balance = balance - 50
MULTISET account:1001:balance new_balanceEXEC# If another client modified account:1001:balance between WATCH and EXEC,# EXEC returns nil (transaction aborted) — retry from WATCHHow it works:
WATCH keymarks the key for monitoring- If any watched key is modified by another client before
EXEC, the transaction is aborted EXECreturnsnilon abort, and the application should retry
This pattern is essential for read-modify-write operations without using a lock.
Q54. What is Redis Pipeline and how does it improve performance? Medium
Pipelining allows sending multiple commands to Redis without waiting for individual replies. Commands are buffered and sent together, then all replies are read at once.
# Without pipeline — N round tripsSET key1 "a" # 1 RTTGET key1 # 1 RTT (total: 2 round trips)
# With pipeline — 1 round trippipeline do SET key1 "a" GET key1end # 1 RTT totalPerformance gain:
- Without pipeline: RTT × N (e.g., 10ms × 1000 = 10 seconds for 1000 commands)
- With pipeline: RTT + server processing time (≈ 1 round trip total)
- Up to 100x improvement in throughput
Important caveats:
- Commands are not executed atomically (unlike MULTI/EXEC transactions)
- Clients must buffer replies in memory
- Too-large pipelines can consume excessive memory
Q55. What is the difference between Redis Pipeline and Transaction (MULTI/EXEC)? Medium
| Feature | Pipeline | Transaction (MULTI/EXEC) |
|---|---|---|
| Atomicity | No — commands may interleave with others | Yes — no other commands run between MULTI/EXEC |
| Batching | Yes — sends commands in batch | Yes — queues commands, then executes |
| Round trips | 1 for entire batch | 1 for entire batch |
| On error | Individual command errors don’t affect others | All-or-nothing (but no rollback inside) |
| Server-side queue | No — sent as fast as possible | Yes — queued until EXEC |
| Use case | Bulk loading, maximizing throughput | Atomic operations, read-modify-write |
You can combine both — use pipeline + MULTI/EXEC for atomic batch operations.
Q56. What are Redis Eviction Policies? Medium
When Redis reaches maxmemory, it evicts keys based on the configured policy:
| Policy | Description |
|---|---|
noeviction | Return error on writes (default) |
allkeys-lru | Evict least recently used keys (most common for caching) |
allkeys-lfu | Evict least frequently used keys |
volatile-lru | Evict LRU only among keys with TTL set |
volatile-lfu | Evict LFU only among keys with TTL |
allkeys-random | Evict random keys |
volatile-random | Evict random keys from TTL-set keys only |
volatile-ttl | Evict keys with shortest TTL first |
Recommendation:
- Caching use case:
allkeys-lru(orallkeys-lfuif access frequency matters) - Redis as primary DB:
noeviction(prevent data loss) - Mixed usage:
volatile-lru(only evict cache keys with TTL)
Q57. What is the LRU vs LFU eviction policy? Medium
LRU (Least Recently Used): Evicts keys that haven’t been accessed for the longest time.
# configmaxmemory-policy allkeys-lruLFU (Least Frequently Used): Evicts keys that are accessed the least often over time.
# configmaxmemory-policy allkeys-lfu| Aspect | LRU | LFU |
|---|---|---|
| Considers | Time since last access | Overall access frequency |
| Good for | Recently accessed = likely needed again | Frequently accessed = likely needed again |
| Weakness | Downloaded once → stays forever | Favorite content → stays forever (stale) |
| Redis support | Since v1.0 | Since v4.0 (with logarithmic frequency counter + decay) |
Redis uses approximate LRU/LFU (not exact) — it samples a few keys and evicts the best candidate. This is much more memory-efficient than tracking full LRU/LFU info for every key.
Q58. What caching patterns are commonly used with Redis? Medium
1. Cache-Aside (Lazy Loading) The application checks Redis first. On miss, reads DB, stores in Redis, returns.
GET from Redis → miss → GET from DB → SET in Redis → return2. Read-Through Cache sits between app and DB. The cache loads data from DB on miss.
3. Write-Through Data is written to cache AND DB simultaneously (synchronous).
4. Write-Behind (Write-Back) Data is written to cache first, then asynchronously persisted to DB.
5. Refresh-Ahead Cache proactively refreshes data before it expires (predictive).
| Pattern | Write Speed | Read Speed | Data Loss Risk | Complexity |
|---|---|---|---|---|
| Cache-Aside | Fast | Miss → slower | None | Low |
| Read-Through | Fast | Miss → slower | None | Medium |
| Write-Through | Slower | Fast | None | Medium |
| Write-Behind | Fastest | Fast | Some (if cache crashes) | High |
Q59. What is the Cache-Aside pattern in detail? Medium
Cache-Aside (Lazy Loading) is the most common caching pattern:
1. App requests data from cache (Redis)2. If cache HIT → return data immediately3. If cache MISS (key doesn't exist or expired): a. Query the database directly b. Store result in Redis with TTL c. Return data to clientasync function getUser(id) { // Try cache first const cached = await redis.get(`user:${id}`); if (cached) return JSON.parse(cached);
// Cache miss — query database const user = await db.query('SELECT * FROM users WHERE id = ?', [id]); if (user) { // Store in cache with TTL await redis.setex(`user:${id}`, 3600, JSON.stringify(user)); } return user;}Pros:
- Only caches data that’s actually requested (cache stays lean)
- Simple to implement
- No stale data penalty beyond TTL
Cons:
- Cache miss penalty (3 network calls instead of 1)
- Initial request after deployment is always slow (cold cache)
- Stale data until TTL expires (if DB is updated directly)
Q60. What is Cache Stampede (Thundering Herd) and how do you prevent it? Medium
Cache Stampede occurs when a popular cache key expires and many concurrent requests all try to regenerate it simultaneously, overwhelming the database.
Prevention strategies:
1. Mutex Lock — only one request regenerates
async function getData(key) { let data = await redis.get(key); if (data) return data;
// Try to acquire lock const lock = await redis.setnx(`lock:${key}`, '1'); if (lock) { await redis.expire(`lock:${key}`, 5); data = await expensiveQuery(); await redis.setex(key, 3600, data); await redis.del(`lock:${key}`); return data; }
// Wait and retry await sleep(10); return getData(key);}2. Early expiration — refresh before expiry Set TTL to e.g. 3600, but treat the key as stale after 3000 seconds. Refresh proactively.
3. Probabilistic early recomputation
Randomly regenerate the cache before expiry based on probability (TTL - elapsed) / TTL.
Q61. What is Redis Replication and how does it work? Medium
Redis Replication allows a replica to be an exact copy of a primary (master) instance.
How it works:
- Replica connects to primary and issues
REPLICAOF master_host master_port - Primary starts a background save (RDB) or uses diskless replication
- Primary sends the RDB file to the replica
- Replica loads the RDB and applies buffered commands
- After sync, replica receives all write commands as a continuous stream
# On replicaREPLICAOF 192.168.1.100 6379
# To break replication and make it a masterREPLICAOF NO ONEKey characteristics:
- Asynchronous by default (primary doesn’t wait for replica ACK)
- One primary can have multiple replicas
- Replicas can have their own replicas (cascading)
- Replicas are read-only by default (configurable)
- Supports partial resynchronization (psync2) — after brief disconnections, replicas can pick up where they left off instead of full resync
Q62. What is Redis Sentinel? Medium
Redis Sentinel provides high availability for Redis without manual intervention:
What Sentinel does:
- Monitoring — Constantly checks if primary and replicas are working
- Notification — Alerts system admins when something goes wrong
- Automatic failover — If primary fails, promotes a replica to primary
- Configuration provider — Clients ask Sentinel for the current primary address
How failover works:
- Sentinel detects primary is down (quorum of sentinels agrees)
- Sentinel elects a leader to perform failover
- Leader promotes one replica to primary
- Leader reconfigures remaining replicas to follow the new primary
- Leader updates the configuration
Minimum setup: 3 Sentinel instances (for quorum) + 1 primary + 2 replicas
# sentinel.confsentinel monitor mymaster 127.0.0.1 6379 2 # 2 = quorumsentinel down-after-milliseconds mymaster 5000sentinel failover-timeout mymaster 60000sentinel parallel-syncs mymaster 1Q63. What is Redis Cluster? Medium
Redis Cluster is a distributed implementation that shards data across multiple nodes automatically.
Key features:
- Automatic sharding — Keys are distributed across 16384 hash slots
- High availability — Each shard can have replicas (Node B can be replica of Node A)
- Decentralized — No central coordinator; nodes communicate via gossip protocol
- Partial failure tolerance — If a primary fails, its replica is promoted
- Minimal setup — At least 3 primary nodes recommended
Hash slot calculation:
HASH_SLOT = CRC16(key) mod 16384Limitations:
- Multi-key operations only work if keys are in the same hash slot (use hash tags:
{user:1001}.name) - Only one database (no
SELECTwith multiple databases) - No cross-slot transactions
- Client must support cluster protocol (MOVED redirections)
# Create a clusterredis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \ --cluster-replicas 1Q64. How does Redis shard data in Cluster mode? Medium
Redis Cluster uses hash slot partitioning:
- 16384 hash slots total
- A key’s hash slot =
CRC16(key) mod 16384 - Each node owns a range of hash slots
Node A: slots 0-5460Node B: slots 5461-10922Node C: slots 10923-16383Hash tags ensure multiple keys land in the same slot:
# Without hash tag — different slotsuser:1001:name → slot 125user:1001:email → slot 211
# With hash tag { } — same slot{user:1001}:name → slot 152{user:1001}:email → slot 152 (same!)When a client connects to the wrong node, it receives a MOVED redirect telling it which node to query next.
Q65. What is the difference between Redis Sentinel and Redis Cluster? Medium
| Feature | Sentinel | Cluster |
|---|---|---|
| Primary purpose | High availability (failover) | Sharding + high availability |
| Data distribution | All nodes have all data (primary-replica) | Data is sharded across nodes |
| Write capacity | Single primary handles all writes | Multiple primaries (one per shard) |
| Multi-key ops | Yes (all keys on same node) | Only within same hash slot |
| Complexity | Lower | Higher |
| Min nodes | 2 (1 primary + 1 replica) | 6 (3 primaries + 3 replicas) for HA |
| Client support | Standard | Cluster-aware (MOVED handling) |
| Automatic failover | Yes (via Sentinel) | Yes (automatic) |
| Use case | < 10GB dataset, HA is main concern | > 10GB dataset, need horizontal scaling |
Rule of thumb: Use Sentinel for datasets under 10-20GB, Cluster for larger datasets.
Q66. What is RDB persistence and what are its pros/cons? Medium
RDB (Redis Database File) performs point-in-time snapshots of the dataset.
# Configurationsave 900 1 # 1 change in 15 min → savesave 300 10 # 10 changes in 5 min → savesave 60 10000 # 10000 changes in 1 min → save
# Manual saveSAVE # Synchronous (blocks)BGSAVE # Asynchronous (fork + child process)Pros:
- Compact single file (
dump.rdb) — easy to backup and transfer - Fastest restart from persistence (loads RDB in memory)
- Minimal performance impact (uses fork()+COW, child does the work)
- Ideal for disaster recovery (send RDB to remote storage)
Cons:
- Can lose data between snapshots (up to N minutes of writes)
BGSAVEuses fork() — can be slow with large datasets (>10GB)- Not ideal if you need minimal data loss (use AOF instead)
Q67. What is AOF persistence and what are its pros/cons? Medium
AOF (Append-Only File) logs every write operation to a file. On restart, Redis replays the log to reconstruct data.
# Configurationappendonly yesappendfilename "appendonly.aof"
# fsync policies (how often to flush to disk):appendfsync always # Every write — most durable, slowestappendfsync everysec # Every second — good compromise (recommended)appendfsync no # OS decides — fastest, least durablePros:
- Most durable — with
everysec, loses at most 1 second of data - Append-only, no corruption issues (Redis can fix truncated files)
- Human-readable (can tail the file to see commands)
- Rewrite mechanism to compact file size
Cons:
- Larger file size than RDB
- Slower restart than RDB (must replay all commands)
- Can be slower than RDB under heavy write loads (with
alwaysfsync)
Q68. How does AOF rewrite work in Redis? Medium
Over time, the AOF file grows as commands accumulate. AOF rewrite creates a compacted version:
How it works:
- Redis forks a child process
- Child reconstructs the current dataset in memory and writes a minimal AOF
- Meanwhile, parent accumulates new commands in a buffer
- When child finishes, parent appends the buffer to the new AOF
- Parent atomically swaps the old AOF with the new one
What rewrite does:
- Instead of
SET key 1,INCR key,INCR key→ justSET key 3 - Removes expired keys
- Drops keys that have been deleted
# Manual triggerBGREWRITEAOF
# Auto-trigger (config)auto-aof-rewrite-percentage 100 # Rewrite if AOF grows by 100%auto-aof-rewrite-min-size 64mb # Minimum size to rewriteIn Redis 7+, AOF rewrite can happen in the background without fork using multi-threaded I/O.
Q69. What is Redis's memory optimization strategy for small data? Medium
Redis uses memory optimization techniques for small aggregate data types:
ziplist encoding: Small Lists, Hashes, and Sorted Sets are stored as ziplists (compact, contiguous memory) instead of linked lists or hash tables:
# Configurable thresholdshash-max-ziplist-entries 512 # Hash → ziplist if ≤ 512 entrieshash-max-ziplist-value 64 # and each entry ≤ 64 byteslist-max-ziplist-size -2 # List → quicklist (memory efficient)zset-max-ziplist-entries 128 # Sorted Set → ziplist if ≤ 128 entrieszset-max-ziplist-value 64 # and each entry ≤ 64 bytesOther optimizations:
- Intset — Small Sets of integers stored as sorted integer array
- Quicklist — Linked list of ziplists for Lists (memory + performance trade-off)
- Shared objects — Small integers (0-9999) are shared, not duplicated
- Memory alignment — Redis aligns data structures for efficient memory access
Use MEMORY DOCTOR to get memory optimization recommendations.
Q70. What is HyperLogLog in Redis? Medium
HyperLogLog is a probabilistic data structure for cardinality estimation (counting unique elements) using very little memory.
PFADD visitors "user:1001" "user:1002" "user:1001" "user:1003"PFCOUNT visitors # Returns ~3 (approximate unique count)
PFADD today "ip:1.2.3.4" "ip:5.6.7.8"PFADD yesterday "ip:1.2.3.4" "ip:9.10.11.12"PFMERGE all_visitors today yesterday # Merge HLLsPFCOUNT all_visitors # ~3 unique IPs across both daysKey characteristics:
- ~12KB memory per key regardless of cardinality
- 0.81% standard error (approximate, not exact)
- Can count up to 2⁶⁴ unique elements
- Merging two HLLs gives your union estimate with no extra error
- Cannot retrieve elements (only count approximation)
Use cases: Unique visitor counting, distinct IP addresses, search term cardinality.
Q71. What is Redis Pub/Sub and what are its limitations? Medium
Redis Pub/Sub implements a publish/subscribe messaging pattern:
# Subscribe to channels (client 1)SUBSCRIBE news:tech news:sports
# Publish messages (client 2)PUBLISH news:tech "New AI breakthrough!"PUBLISH news:sports "World Cup finals today"Key features:
- Fire-and-forget — Publisher sends, doesn’t wait for subscribers
- Fan-out — All subscribers of a channel get the message
- Pattern subscriptions —
PSUBSCRIBE news:*to subscribe to all news channels
Limitations:
- No message persistence — If subscriber is offline, messages are lost
- No message acknowledgment — No guarantee of delivery
- No backpressure — Slow subscribers can cause buffer overflow
- Not suitable for reliable messaging — Use Redis Streams instead
- Scaling — Every subscriber gets every message (no consumer groups)
Q72. What are Redis Streams and how are they different from Pub/Sub? Medium
Redis Streams are an append-only log data structure introduced in Redis 5.0:
# Add entry to stream (auto-generated ID)XADD mystream * sensor "temp" value 25.5
# Read from streamXRANGE mystream - + # All entriesXREAD COUNT 10 STREAMS mystream 0 # First 10 entries
# Create consumer groupXGROUP CREATE mystream mygroup $XREADGROUP GROUP mygroup consumer1 COUNT 1 STREAMS mystream >Key differences from Pub/Sub:
| Feature | Pub/Sub | Streams |
|---|---|---|
| Persistence | None | Persistent (in-memory, optionally AOF/RDB) |
| Delivery guarantee | At-most-once | At-least-once (with consumer ack) |
| Consumer groups | No | Yes (load balancing across consumers) |
| Backlog | No | Yes (configurable max length) |
| Replay | No | Yes (read from any point) |
| Blocking reads | Yes (SUBSCRIBE blocks) | Yes (XREAD BLOCK) |
Use Streams when you need: Reliable messaging, event sourcing, activity feeds, job queues with acknowledgments.
Q73. How do Consumer Groups work in Redis Streams? Medium
Consumer Groups enable load-balanced message processing across multiple consumers:
Stream: order-eventsConsumer Group: email-group ├── Consumer: worker-1 (processing order:1001, order:1002) ├── Consumer: worker-2 (processing order:1003) └── Consumer: worker-3 (idle)# Create groupXGROUP CREATE order-events email-group $
# Worker reads (new messages assigned round-robin)XREADGROUP GROUP email-group worker-1 COUNT 1 BLOCK 5000 STREAMS order-events >
# Acknowledge after processingXACK order-events email-group 1700000000000-0Features:
- Messages are distributed round-robin among consumers
- Each message is delivered to only one consumer
- Unacknowledged messages remain in the Pending Entries List (PEL)
XPENDINGshows pending messages;XCLAIMreassigns them if a consumer fails- Consumers can reconnect and continue from where they left off
Q74. What is the difference between `BRPOP` and `XREAD` for implementing queues? Medium
| Feature | List-based Queue (BRPOP) | Stream-based Queue (XREAD) |
|---|---|---|
| Blocking read | Yes (BRPOP) | Yes (XREAD BLOCK) |
| Multiple consumers | Each gets different items (but no ack) | With consumer groups, each gets different items + ack |
| Delivery guarantee | At-most-once | At-least-once (with XACK) |
| Replay | No (popped items are gone) | Yes (read from any ID) |
| Message backlog | Single list | Configurable maxlen |
| Multiple subscriber groups | No | Yes (fan-out to different groups) |
| Complexity | Simple | More features, more complex |
# Simple queue with ListRPUSH taskqueue "job1" "job2"BRPOP taskqueue 0 # Blocks and returns "job1"
# Reliable queue with StreamsXADD taskqueue * type "email" to "user@example.com"XGROUP CREATE taskqueue workers $XREADGROUP GROUP workers worker1 COUNT 1 BLOCK 0 STREAMS taskqueue ># Process and acknowledgeXACK taskqueue workers 1700000000000-0Rule of thumb: Use Lists for simple FIFO where losing a message is acceptable. Use Streams for reliable message processing.
Q75. How do you implement a distributed lock with Redis? Medium
A basic distributed lock uses SET NX + EX:
// Acquire lock (atomically — both NX and EX in one command)const lock = await redis.set(`lock:resource`, 'my-instance-id', { NX: true, // Only set if key doesn't exist EX: 10 // Auto-release after 10 seconds (safety)});
if (lock) { try { // Critical section await doWork(); } finally { // Release lock (only if it's still our lock) const script = ` if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) end return 0 `; await redis.eval(script, 1, 'lock:resource', 'my-instance-id'); }}Why Lua script for release? To ensure we only delete OUR lock — checking the value and deleting must be atomic.
Important considerations:
- Set a lock timeout (TTL) so locks don’t persist if the holder crashes
- Use a unique ID (UUID) so only the lock holder can release it
- Add a safety margin — work should complete before TTL expires
- For mission-critical locks, consider Redlock algorithm
Q76. What is the Redlock algorithm? Medium
Redlock is a distributed lock algorithm proposed by Redis creator Antirez for scenarios where locks must be safe across multiple Redis nodes.
How it works:
- Get the current time (T1)
- Acquire lock on N/2 + 1 independent Redis nodes sequentially (e.g., 3 of 5 nodes)
- If acquired majority within lock validity time → lock is held
- If not → release all locks and retry
// Simplified Redlock with 3 Redis instancesconst instances = [redis1, redis2, redis3];const lockKey = 'resource:lock';const lockValue = uuid();const ttl = 10000; // 10 seconds
async function acquireLock() { const startTime = Date.now(); let acquired = 0;
for (const instance of instances) { const result = await instance.set(lockKey, lockValue, 'NX', 'PX', ttl); if (result === 'OK') acquired++; }
const elapsed = Date.now() - startTime; if (acquired >= 2 && elapsed < ttl) { return lockValue; // Lock acquired! }
// Release all locks await releaseLock(lockValue); return null;}Controversy: Redlock has been debated by distributed systems experts (Martin Kleppmann). For most applications, single-node Redis with WATCH or a basic lock is sufficient.
Q77. What is Lua scripting in Redis? Medium
Redis allows running Lua scripts on the server using EVAL:
# Simple Lua scriptEVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 mykey "hello"
# Atomic compare-and-deleteEVAL " if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) end return 0" 1 mykey "expected-value"Why Lua scripts are powerful:
- Atomicity — The entire script runs atomically (no other commands interleaved)
- Reduced network — Send logic to data instead of data to logic
- Complex operations — Implement multi-key logic atomically
- Reusable — Load with
SCRIPT LOADand run withEVALSHA
# Load script and run by hashSCRIPT LOAD "return redis.call('GET', KEYS[1])"# Returns: "4e6d..." (SHA1 hash)EVALSHA "4e6d..." 1 mykeyNote: Lua scripts are replicated in replication/Cluster — they run on the primary and replicas (unless
redis.replicate_commands()is used).
Q78. What are the best practices for Redis key naming? Medium
1. Use colons for namespacing
user:1001:profileuser:1001:sessions2. Use consistent singular forms
// Avoid mixinguser:1001:nameusers:1001:name ❌3. Keep keys short but readable
u:1001:n ❌ Too crypticuser:1001:name ✅ Good4. Include the ID of the entity
article:42:comments5. For Redis Cluster, use hash tags for related keys
{user:1001}:profile{user:1001}:sessions # Same hash slot!6. Avoid overly long keys (>100 bytes wastes memory)
7. Use a consistent prefix per application (especially when sharing a Redis instance)
app1:user:1001app2:user:1001Q79. How do you monitor Redis performance? Medium
Key tools and commands:
1. INFO command — Overall server health
INFO stats → total_commands_processed, instantaneous_ops_per_secINFO memory → used_memory, fragmentation_ratioINFO clients → connected_clients, blocked_clients2. SLOWLOG — Find slow queries
SLOWLOG GET 10 # Last 10 slow queriesSLOWLOG LEN # Number of slow queriesSLOWLOG RESET # Clear slow log# Config: slowlog-log-slower-than 10000 (microseconds)3. MONITOR — Real-time command stream (debugging only, not for production)
MONITOR # Shows every command as it executes4. LATENCY — Measure latency
LATENCY LATEST # Latest latency eventsLATENCY HISTORY command # Latency history for a commandLATENCY DOCTOR # Suggestions for latency issues5. MEMORY DOCTOR — Memory optimization recommendations
MEMORY DOCTOR6. Grafana + Prometheus — For production monitoring dashboards
Key metrics to watch:
hit_ratio(keyspace_hits / (keyspace_hits + keyspace_misses))evicted_keys— sustained evictions indicate maxmemory is too lowrejected_connections— too many clientsfragmentation_ratio— > 1.5 may indicate memory issues
Q80. What is the `SLOWLOG` in Redis and how do you use it? Medium
SLOWLOG records commands that exceed a configured execution time threshold:
# Configure threshold (microseconds)CONFIG SET slowlog-log-slower-than 10000 # Log commands slower than 10ms
# View recent slow commandsSLOWLOG GET 5# 1) 1) (integer) 14 # Unique ID# 2) (integer) 1700000000 # Unix timestamp# 3) (integer) 15000 # Execution time (microseconds)# 4) 1) "KEYS" # Command# 2) "*" # Arguments# 5) "127.0.0.1:6379" # Client
# Number of slow queriesSLOWLOG LEN
# Clear slow logSLOWLOG RESETCommon sources of slow queries:
KEYS *(use SCAN instead)SMEMBERSon large Sets (use SSCAN)SORTwith large datasetsHGETALLon large HashesZRANGEon large Sorted Sets with very wide ranges
Q81. What is the `MONITOR` command and why should you avoid it in production? Medium
MONITOR streams every command processed by Redis in real-time:
redis> MONITOR1700000000.000000 [0 127.0.0.1:6379] "SET" "mykey" "hello"1700000000.000001 [0 127.0.0.1:6379] "GET" "mykey"Why avoid in production:
MONITORcan reduce Redis throughput by up to 50% or more- Every command is output, creating high overhead
- The output buffer for the MONITOR client can grow and cause memory issues
- It’s a debugging tool, not a production monitoring tool
Alternatives:
- Use
SLOWLOGto find problematic queries - Use Redis’s built-in
INFO statsfor command counts - Enable Redis’s command statistics with
CONFIG SET commandstats yes
Q82. How does memory fragmentation occur in Redis? Medium
Memory fragmentation occurs when allocated memory can’t be fully utilized due to gaps between allocated blocks.
In Redis, fragmentation happens because:
- Redis allocates and deallocates memory frequently
- Different key sizes create non-contiguous usage patterns
- The allocator (jemalloc by default) can’t perfectly fit every allocation
# Check fragmentation ratioINFO memory# used_memory_rss / used_memory = fragmentation ratio
# Example:used_memory: 1.5GB # Actual data sizeused_memory_rss: 2.5GB # RSS (what the OS has allocated)# Fragmentation ratio: 1.67 (2.5/1.5) — elevated!Interpreting fragmentation ratio:
- ≈ 1.0 — Ideal (RSS ≈ actual memory)
- > 1.5 — Significant fragmentation (consider restart or
MEMORY PURGE) - < 1.0 — Memory overcommit (some memory is shared/swapped)
Solutions:
- Restart Redis (frees all memory)
MEMORY PURGE(ask allocator to reclaim memory- Use
jemalloc(Redis default, good fragmentation characteristics) - Align key sizes where possible
Q83. What is Redis `CONFIG SET` and `CONFIG GET`? Medium
CONFIG GET pattern— Returns configuration parameters matching a glob patternCONFIG SET parameter value— Changes configuration at runtime (without restart)
# Get configurationCONFIG GET maxmemory # * "100mb"CONFIG GET *lru* # All LRU-related configsCONFIG GET * # ALL configuration (be careful — huge output!)
# Set configuration at runtimeCONFIG SET maxmemory 2gbCONFIG SET maxmemory-policy allkeys-lruCONFIG SET slowlog-log-slower-than 5000Important:
- Runtime changes with
CONFIG SETare temporary — lost on restart - To make permanent, update
redis.conf - Use
CONFIG REWRITEto save current runtime config toredis.conf
Q84. What is the difference between Redis `EXPIRE` and `PEXPIRE`? Medium
Both set key expiration, just with different time units:
EXPIRE key seconds— Time-to-live in seconds (integer)PEXPIRE key milliseconds— Time-to-live in milliseconds (integer)
EXPIRE mykey 60 # Expires in 60 secondsPEXPIRE mykey 60000 # Same: 60000 milliseconds = 60 secondsSimilarly:
TTL key— Returns remaining time in secondsPTTL key— Returns remaining time in millisecondsSETEX key seconds value— Set with TTL in secondsPSETEX key milliseconds value— Set with TTL in milliseconds
PEXPIRE and PTTL are useful when you need finer granularity.
Q85. How does Redis handle key expiration? When are expired keys removed? Medium
Redis uses two strategies for expiring keys:
1. Passive Expiration (Lazy)
- When a key is accessed (GET, EXISTS, etc.), Redis checks if it’s expired
- If expired, it’s deleted and nil is returned
- This means expired keys that are never accessed consume memory
2. Active Expiration (Periodic)
- Every 100ms, Redis runs a background cycle:
- Samples 20 random keys with TTL
- Deletes all expired keys found
- If >25% of sampled keys were expired, repeat from step 1
- Repeats for maximum 25% of the time slice (so it doesn’t block)
- This bounds the memory used by expired-but-unaccessed keys
Key point: The EXPIRE command doesn’t immediately delete the key — it marks it for eventual deletion. Redis guarantees that expired keys won’t be returned, but they may still exist in memory briefly.
Q86. What is the `OBJECT` command in Redis? Medium
The OBJECT command inspects Redis keys internally:
# Internal encoding (how the value is stored)OBJECT ENCODING mykey# "embstr" — small string# "raw" — large string# "ziplist" — small hash/list/zset# "hashtable" — large hash/set# "intset" — integer set
# Reference count (how many keys share this value)OBJECT REFCOUNT mykey# Numbers > 1 indicate shared objects (small integers)
# Idle time in secondsOBJECT IDLETIME mykey# How many seconds since the key was last accessed (used by LRU)
# Memory usage (approximate)OBJECT FREQ mykey # LFU frequency counter (logistic)Use OBJECT ENCODING to verify internal optimizations are working (e.g., confirming small Hashes use ziplist).
Q87. What is a Redis "watchdog" and how do you use it? Medium
The Redis latency watchdog records the stack trace of Redis’s main thread when it detects a command taking too long:
# Enable (time in milliseconds)CONFIG SET watchdog-period 100 # Log stack trace if command takes > 100ms
# View logs (in server log, not Redis CLI)# "Slow operation detected, watchdog fired."# Shows the code path causing the latency
# DisableCONFIG SET watchdog-period 0Use case: Identifying exactly which code path is causing latency spikes. Unlike SLOWLOG (which shows the command + arguments), the watchdog shows the internal function call stack.
Note: The watchdog is a debugging tool and should be used carefully in production as it can affect performance.
Q88. What is the difference between synchronous and asynchronous replication in Redis? Medium
Asynchronous Replication (Default):
- Primary executes write commands and immediately returns to client
- Replication to replicas happens in the background
- If primary crashes before replica receives the data, that data is lost
- Much better performance — primary doesn’t wait for replicas
# Default behaviorSET key value # Returns immediately, replication is asyncSynchronous Replication (WAIT command):
WAITblocks the client until a specified number of replicas acknowledge the write- Provides stronger durability guarantee
- Higher latency — primary waits for replica ACK
MULTISET critical_key "important_value"EXECWAIT 1 1000 # Wait for at least 1 replica to ACK, timeout 1000msTrade-off: Asynchronous = faster but potential data loss. WAIT = stronger durability but higher latency. Even with WAIT, Redis doesn’t guarantee full synchronous replication like some traditional databases (it’s “best effort”).
Q89. What is Redis `CLIENT` command used for? Medium
The CLIENT command manages client connections:
# List all connected clientsCLIENT LIST# id=3 addr=127.0.0.1:6379 ... cmd=GET
# Kill a client connectionCLIENT KILL addr 127.0.0.1:56789
# Get/set the current connection nameCLIENT SETNAME my-app-workerCLIENT GETNAME # "my-app-worker"
# Pause all clients for a duration (testing failovers)CLIENT PAUSE 10000 # Pause all clients for 10 seconds
# Set a max number of client connectionsCONFIG SET maxclients 10000
# Kill idle connectionsCLIENT KILL TYPE idleUse CLIENT LIST to see blocked clients, idle connections, and the commands currently being executed.
Q90. What is a "keyspace notification" in Redis? Medium
Keyspace notifications allow clients to subscribe to events that affect keys (expiry, eviction, modification):
# Enable notifications (in redis.conf or CONFIG SET)CONFIG SET notify-keyspace-events KEA# K = keyspace events, E = keyevent events, A = all
# Subscribe to all expired key eventsPSUBSCRIBE __keyevent@0__:expired
# Subscribe to key modifications in a specific namespacePSUBSCRIBE __keyspace@0__:user:*Event types:
__keyspace@0__:mykey— Pattern:__keyspace@<db>__:<key>__keyevent@0__:del— Pattern:__keyevent@<db>__:<operation>
Notification types:
| Type | Events |
|---|---|
set | Key created/updated |
expired | Key TTL expired |
evicted | Key evicted by maxmemory |
del | Key deleted |
rename_from/to | Key renamed |
Use case: Cache invalidation, session cleanup, triggering background jobs when data expires.
Note: Notifications are fire-and-forget (no persistence. If no subscriber is listening, events are lost).
Q91. What is the difference between a Redis transaction and a Lua script? Medium
| Feature | MULTI/EXEC Transaction | Lua Script (EVAL) |
|---|---|---|
| Atomicity | Yes — commands run sequentially, no interleaving | Yes — entire script runs atomically |
| Conditional logic | No — all commands execute (no IF/ELSE) | Yes — full control flow (if, while, for) |
| Data returned | Only last command result | Full script return value |
| Replication | Commands replicate individually | Script replicates as one unit (or with redis.replicate_commands()) |
| Complexity | Simple read-modify-write | Complex multi-step operations |
| Input from earlier commands | No — can’t use result of GET in a later SET | Yes — variables hold intermediate results |
When to use each:
- MULTI/EXEC: Simple atomic batches where you just need to run several commands together
- Lua scripts: Complex operations with conditions, loops, or where one command’s result determines the next command
Q92. How does Redis handle failover in a Cluster? Medium
Redis Cluster handles failover automatically:
Failure detection:
- Each node sends PING/PONG to other nodes (gossip protocol)
- A node is marked PFAIL (possibly failed) if another node can’t reach it
- If majority of primaries agree it’s PFAIL, it becomes FAIL
Failover process:
- A replica of the failed primary initiates the failover
- The replica votes among all primaries to become the new primary
- If it gets majority votes, it becomes the new primary
- The new primary starts accepting writes
- When the old primary comes back, it becomes a replica of the new primary
Automatic failover conditions:
- The replica’s primary is marked as FAIL
- The replica’s data is reasonably up-to-date (within a configurable threshold)
- The replica has a higher “rank” (more complete replication)
Manual failover (maintenance):
CLUSTER FAILOVER # Graceful — replica becomes primaryCLUSTER FAILOVER FORCE # Force without grace periodCLUSTER FAILOVER TAKEOVER # Emergency, no consent neededQ93. What is the role of the Redis Cluster bus? Medium
The Cluster bus is a separate communication channel between Redis Cluster nodes:
- Port: Base port + 10000 (e.g., 6379 → 16379)
- Protocol: TCP-based binary protocol (not Redis protocol)
- Purpose: Internal cluster management
What travels on the cluster bus:
- Gossip messages — Nodes share information about other nodes (PING/PONG)
- Configuration propagation — Hash slot ownership changes
- Failure detection — PFAIL/FAIL status propagation
- Voting — During failover elections
- Replica synchronization — Partial sync coordination
The cluster bus is critical for cluster operation — if the bus port is blocked by a firewall, nodes can’t communicate and the cluster will fail.
Q94. How do you handle failover in a Redis Sentinel setup? Medium
Sentinel failover process:
1. Subjective Down (SDOWN):
- A single Sentinel marks the primary as SDOWN if it’s unreachable for
down-after-milliseconds
2. Objective Down (ODOWN):
- Other Sentinels are queried
- If
quorumnumber of Sentinels agree the primary is down → ODOWN
3. Leader Election:
- Sentinels vote to elect a leader to perform the failover
- The first Sentinel to reach
quorumvotes becomes leader
4. Failover Execution:
- Leader selects the best replica to promote (based on priority, replication offset, run ID)
- Leader sends
SLAVEOF NO ONEto the chosen replica - Leader reconfigures other replicas to follow the new primary
- Leader updates the epoch (version number for configuration)
5. Client reconnection:
- Clients should use Sentinel to discover the new primary
SENTINEL get-master-addr-by-name mymaster
Q95. What is the difference between Redis `MULTI` and `Pipeline`? Medium
| Aspect | MULTI/EXEC (Transaction) | Pipeline |
|---|---|---|
| Atomicity | ✅ Yes — commands execute as a unit | ❌ No — commands may interleave with other clients |
| Execution | Server queues until EXEC, then runs all | Server processes immediately as received |
| Errors | Syntax errors reject entire batch; run-time errors don’t stop others | Each command may fail independently |
| Memory | Server queues commands | Client queues commands (server processes immediately) |
| Round trips | 2 (MULTI + EXEC) plus N commands | 1 (all commands sent in batch) |
| WATCH support | ✅ Yes | ❌ No |
Conclusion:
- Use Pipeline when you need performance (independent commands, no atomicity needed)
- Use MULTI/EXEC when you need atomicity (critical operations, read-modify-write)
- Use Pipeline + MULTI/EXEC for the best of both worlds (atomic batch with a single round trip)
Q96. What is `RPOPLPUSH` and how is it used? Medium
RPOPLPUSH source destination atomically removes the last element from source and pushes it to the start (left) of destination:
RPUSH queue "task1" "task2" "task3"RPOPLPUSH queue processing_queue# Returns "task3" (popped from queue)# queue: ["task1", "task2"]# processing_queue: ["task3"]Use case — Reliable Queue:
- Worker pops from the main queue and pushes to a processing queue (
RPOPLPUSH) - After successful processing, the worker removes from the processing queue (
LREM) - If the worker crashes, the task remains in the processing queue (can be retried)
BRPOPLPUSH is the blocking version — waits for an element if the queue is empty.
In Redis 7+, BRPOPLPUSH is deprecated in favor of BLMOVE:
BLMOVE queue processing_queue RIGHT LEFT 0Q97. What is the `SORT` command in Redis? Medium
The SORT command sorts the elements of a List, Set, or Sorted Set:
RPUSH scores 50 30 80 10 60SORT scores # Returns [10, 30, 50, 60, 80]
# DescendingSORT scores DESC # [80, 60, 50, 30, 10]
# Limit resultsSORT scores LIMIT 0 3 # [10, 30, 50] (first 3)
# Sort by external keys (sorting by a related hash field)SORT user:ids BY user:*->age GET user:*->nameAdvanced SORT (BY/GET):
# Given user IDs: [1, 2, 3]# And hashes: user:1 (name: "Alice", age: 30), user:2 (...)SORT user:ids BY user:*->age GET user:*->name# Returns user names sorted by ageWarning:
SORTcan be slow on large datasets. Consider using Sorted Sets instead.SORTis being deprecated in Redis 7+ in favor ofSORT_RO(read-only) and explicit data structure commands.
Q98. What is the `GEOADD` command and how do you use Redis for geospatial queries? Medium
Redis has built-in geospatial commands using Sorted Sets:
# Add locationsGEOADD cafes 13.361389 38.115556 "Palermo" # longitude latitude nameGEOADD cafes 15.087269 37.502669 "Catania"
# Distance between two locations (km, m, mi, ft)GEODIST cafes "Palermo" "Catania" km # "166.27"
# Find locations within radiusGEORADIUS cafes 15 37 100 km# Returns ["Catania", "Palermo"]
# Get coordinatesGEOPOS cafes "Palermo" # ["13.361389", "38.115556"]
# Get geohash stringGEOHASH cafes "Palermo" # "sqc8b49rny0"
# GEORADIUSBYMEMBER — radius from existing memberGEORADIUSBYMEMBER cafes "Palermo" 200 kmUnder the hood: Geospatial indexes are stored as Sorted Sets with geohash-encoded scores. This means you can also use ZSET commands on geospatial data.
Use cases: Find nearby restaurants, ride-hailing dispatch, location-based recommendations.
Q99. What is `MIGRATE` command in Redis? Medium
MIGRATE atomically moves a key from one Redis instance to another:
# Move key 'mykey' to another Redis instanceMIGRATE 192.168.1.50 6379 mykey 0 5000 COPY REPLACEArguments:
host port— Target Redis instancekey|""— Key to migrate (or""with KEYS)destination-db— Database index on targettimeout— Timeout in msCOPY— Keep the key on the source (don’t delete)REPLACE— Replace key on target if it existsKEYS key [key ...]— Migrate multiple keys (Redis 3.0.6+)
How it works:
- Source serializes the key (RDB format)
- Sends data to target
- Target acknowledges
- Source deletes the key (unless COPY is specified)
Use case: Moving data between Redis instances, resharding in Cluster, zero-downtime data migration.
Q100. What is the `DUMP` and `RESTORE` commands? Medium
DUMP key— Serializes the key’s value in Redis internal format (returns a binary string)RESTORE key ttl serialized-value— Creates a key from the serialized value
# On source serverDUMP user:42# Returns: "\x00\x15user_data_here\x06\x00\x8f..."
# On target server (can be different Redis instance)RESTORE user:42 0 "\x00\x15user_data_here\x06\x00\x8f..."# TTL = 0 means no expiration (persistent key)Use cases:
- Key-level migration — Move a single key without
MIGRATE - Backup specific keys — Dump important keys for safekeeping
- Copy between environments — Restore production data to staging
Limitations:
RESTOREreplaces existing keys (useREPLACEoption)- Format may differ between Redis versions (backward compatible within the same major version)
- Only works for a single key at a time
Q101. How does Redis handle `maxmemory` and what happens when it's reached? Medium
When Redis reaches the maxmemory limit:
- Writes trigger eviction logic (reads still work fine)
- Redis tries to evict keys based on the configured
maxmemory-policy - If no keys can be evicted (noeviction policy) → write commands return an error
CONFIG SET maxmemory 1gbCONFIG SET maxmemory-policy allkeys-lruCommands that can fail when memory is full:
- All write commands (SET, LPUSH, SADD, HSET, etc.)
EXPIRE(modifies TTL metadata)RENAMEGETSET(modifies value)
Commands that still work:
- Read commands (GET, LRANGE, SMEMBERS, etc.)
DEL(frees memory)UNLINK(non-blocking delete)FLUSHDB,FLUSHALL(clear all data)
Monitoring:
INFO stats# evicted_keys: 1500 ← keys evicted since startupA high or rapidly increasing evicted_keys count indicates the cache is too small or the eviction policy needs adjustment.
Q102. What is `UNLINK` and how is it different from `DEL`? Medium
DEL key— Synchronously deletes the key (blocks until memory is freed)UNLINK key— Asynchronously deletes (unlinks) the key (returns immediately)
DEL bigkey # Blocks Redis for potentially secondsUNLINK bigkey # Returns immediately, freed in backgroundWhy UNLINK exists:
- Deleting a key with a large value (e.g., a List with millions of elements) can block Redis for seconds
UNLINKperforms the O(1) unlinking part (removing from keyspace) immediately- The actual memory reclamation (O(N)) happens in a background thread
When to use each:
DEL— Small keys, or when you want deterministic behaviorUNLINK— Large keys (> 10,000 elements), or when you must avoid blocking
In Redis 6+, FLUSHALL ASYNC and FLUSHDB ASYNC use the same background thread approach.
Q103. What is the purpose of `SCAN` and how does it work? Medium
SCAN iterates over keys in the database incrementally without blocking Redis:
SCAN 0 MATCH user:* COUNT 100# 1) "123" ← cursor for next call# 2) 1) "user:42" ← matching keys (up to 100)# 2) "user:1001"# ...
SCAN 123 MATCH user:* COUNT 100 # Continue from cursor 123# ... continues until cursor returns "0" (iteration complete)Key points:
- Non-blocking — Only processes a small number of keys per call (unlike
KEYS) - Cursor-based — Returns a cursor to continue in the next call (cursor “0” = done)
- Not guaranteed to return all keys (keys added during iteration may be missed or duplicated)
- Each call guarantees about
COUNTkeys are checked (returned may be less)
Type-specific variants:
SSCAN key cursor [MATCH] [COUNT]— Iterate Set membersHSCAN key cursor [MATCH] [COUNT]— Iterate Hash fieldsZSCAN key cursor [MATCH] [COUNT]— Iterate Sorted Set members
Always use
SCANinstead ofKEYSin production!
Q104. What is the Redis `EVAL` command and how do you pass keys/args? Medium
EVAL script numkeys key [key ...] arg [arg ...] executes a Lua script:
EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 mykey "hello"Key/arg convention:
KEYS[1..N]— Redis keys (required for Cluster compatibility)ARGV[1..N]— Additional arguments (non-key values)
EVAL " local current = redis.call('GET', KEYS[1]) if not current then redis.call('SET', KEYS[1], ARGV[1]) return ARGV[1] end return current" 1 mykey "default_value"Why separate KEYS and ARGV?
- In Redis Cluster, the hash slot is computed from keys — Redis needs to know which keys are used
- Documentation clarity — easier to understand which are keys vs values
- Redis can optimize command routing in Cluster
Available libraries in Redis Lua:
redis.call()— Returns error on failureredis.pcall()— Returns error object on failure (catchable)redis.log()— Write to Redis logredis.sha1hex()— SHA1 hashredis.status_reply(),redis.error_reply()— Create responses
Q105. What is the `SCRIPT LOAD` and `EVALSHA` commands? Medium
Instead of sending the full script source every time, you can load a script once and run it by its SHA1 hash:
# Load the script and get its SHA1 hashSCRIPT LOAD "return redis.call('GET', KEYS[1])"# Returns: "4e6d2e36c84283d7c9f8eef1e6f0e27c9f8eef12"
# Run by hash (much less bandwidth!)EVALSHA 4e6d2e36c84283d7c9f8eef1e6f0e27c9f8eef12 1 mykeyHow scripts are cached:
- Scripts are cached forever on the server (until SCRIPT FLUSH)
- Cached across all databases
- Script cache is not persisted (lost on restart) — clients must handle
NOSCRIPTerrors by falling back toEVAL - Use
SCRIPT EXISTS [sha1 ...]to check if scripts are cached
// Pattern: Try EVALSHA first, fallback to EVALtry { return await redis.evalsha(scriptHash, keys, args);} catch (e) { if (e.code === 'NOSCRIPT') { return await redis.eval(scriptSource, keys, args); } throw e;}Q106. How do you handle Redis connection management from an application? Medium
Best practices for Redis connection management:
1. Connection Pooling Use a connection pool to avoid creating/destroying connections frequently:
// Node.js (ioredis)const Redis = require('ioredis');const redis = new Redis.Cluster([ { host: '127.0.0.1', port: 7000 }], { redisOptions: { maxRetriesPerRequest: 3, enableReadyCheck: true, retryStrategy(times) { return Math.min(times * 50, 2000); // Exponential backoff } }});2. Handle Disconnections Gracefully Always implement reconnect logic:
- Use the client’s built-in reconnect (most clients have it)
- Implement exponential backoff
- Log reconnection attempts
- Have a circuit breaker pattern for when Redis is down
3. Use Separate Connections for Different Purposes
const cacheClient = new Redis({ port: 6379 }); // For cache operationsconst pubClient = new Redis({ port: 6379 }); // For Pub/Subconst subClient = pubClient.duplicate(); // For subscribing4. Set Reasonable Timeouts
const client = new Redis({ connectTimeout: 10000, // 10 seconds to connect commandTimeout: 5000, // 5 seconds for commands keepAlive: 10000 // TCP keepalive every 10 seconds});5. Monitor Connection Health
redis.on('error', (err) => console.error('Redis error:', err));redis.on('connect', () => console.log('Connected to Redis'));redis.on('ready', () => console.log('Redis ready'));Q107. What is Redis `SUBSCRIBE` and `PSUBSCRIBE`? Medium
SUBSCRIBE channel [channel ...]— Subscribe to one or more specific channelsPSUBSCRIBE pattern [pattern ...]— Subscribe to channels matching a glob pattern
# Subscribe to specific channelsSUBSCRIBE news:tech news:sports
# Subscribe to patternsPSUBSCRIBE news:* # All news channelsPSUBSCRIBE user:*:profile # All user profile channelsPattern matching rules:
*— Matches any number of characters?— Matches a single character[abc]— Matches any character in the set
Unsubscribing:
UNSUBSCRIBE news:tech # Unsubscribe from one channelUNSUBSCRIBE * # Unsubscribe from allPUNSUBSCRIBE news:* # Unsubscribe from patternMessage format (SUBSCRIBE mode):
1) "message" or "pmessage"2) "news:tech" "news:*" (pattern matched)3) "Hello!" "news:tech" (actual channel)4) "Hello!" (message)Q108. What is the difference between `SUBSCRIBE` and `PSUBSCRIBE` in Redis? Medium
| Feature | SUBSCRIBE | PSUBSCRIBE |
|---|---|---|
| Match type | Exact channel name | Glob pattern |
| Example | SUBSCRIBE news:tech | PSUBSCRIBE news:* |
| Performance | Slightly faster | Slightly slower (pattern matching overhead) |
| Memory | One entry per subscribed channel | One entry per pattern (matches many channels) |
| Flexibility | Must list every channel | One pattern catch all matching channels |
# SUBSCRIBE: receive messages from these specific channelsSUBSCRIBE orders:create orders:update orders:delete
# PSUBSCRIBE: receive from any orders:* channelPSUBSCRIBE orders:*When to use each:
SUBSCRIBE— Known, specific channels (orders:create, orders:update)PSUBSCRIBE— Dynamic channel names or wildcard subscriptions (user:123:activity)
Note: A message that matches both a
SUBSCRIBEand aPSUBSCRIBEwill be received twice — once for each subscription type.
Q109. What is the `PUBSUB` command used for? Medium
PUBSUB is an introspection command for the Pub/Sub system:
# List active channels (channels with at least one subscriber)PUBSUB CHANNELSPUBSUB CHANNELS news:* # Filter by pattern
# Count subscribers for specific channelsPUBSUB NUMSUB news:tech news:sports# 1) "news:tech" 2) (integer) 3 # 3 subscribers# 3) "news:sports" 4) (integer) 1 # 1 subscriber
# Count pattern subscriptionsPUBSUB NUMPAT# (integer) 5 # 5 active PSUBSCRIBE patternsUse cases:
- Monitoring Pub/Sub activity
- Debugging subscription leaks (unexpected growth)
- Capacity planning (determining fan-out)
Note:
PUBSUB CHANNELSwithout a pattern returns ALL active channels, which can be expensive with many channels.
Q110. What is the `WAIT` command in Redis? Medium
WAIT numreplicas timeout blocks the current client until at least numreplicas replicas have acknowledged the preceding write commands:
SET critical_data "important"WAIT 1 1000 # Wait for at least 1 replica to confirm, timeout 1000msReturn value: The number of replicas that acknowledged within the timeout period.
WAIT 2 5000 # Returns: 2 (both replicas acked) or 1 (only one acked before timeout)Key points:
- Only waits for writes that were executed before the
WAITcommand WAITdoes NOT guarantee fully synchronous replication (it’s “best effort”)- The
timeoutis per-write-group, not total time WAIT 0 0returns immediately with the current number of connected replicas- Higher durability at the cost of higher latency
Use case: Ensuring critical data (payment transactions, audit logs) is replicated before proceeding.
🔴 Hard (Q111–Q155)
Section titled “🔴 Hard (Q111–Q155)”Q111. How does Redis's single-threaded event loop handle concurrency internally? Hard
Redis uses the ae event loop (based on epoll/kqueue) with a single thread for command execution:
Client 1 → epoll → [SET key1 val1] ↓Client 2 → epoll → [GET key2] ↓Client 3 → epoll → [INCR counter]Architecture:
- I/O multiplexing thread (Redis 6+) — Handles socket reads/writes in background threads
- Main thread — Executes commands sequentially from the queue
- Background threads (Redis 4+) — Handle lazy freeing (UNLINK), AOF fsync, and close operations
Why single-threaded is fast:
- No context switching overhead
- No lock contention
- All data in CPU cache-friendly access patterns
- 99.9% of operations are pure memory operations
- Network I/O is offloaded to background threads (Redis 6+)
Redis 6+ I/O threads:
io-threads 4 # Number of I/O threads (default: 1 = no I/O threading)io-threads-do-reads yes # Also use threads for readsI/O threads handle serialization/deserialization, but command execution is always single-threaded.
Q112. What is the internal memory layout of a Redis String? Hard
Redis Strings use two possible internal encodings:
1. embstr (Embedded String):
- For strings up to 44 bytes (Redis 7+) / 39 bytes (earlier versions)
- String data is stored contiguous with the Redis object header
- Single memory allocation
- Cache-friendly (data and header in same cache line)
- Read-only — modifying requires conversion to raw
2. raw (Raw String):
- For strings > 44 bytes
- Two memory allocations (header + data buffer)
- Supports modifications (APPEND, SETRANGE, GETRANGE)
- Uses SDS (Simple Dynamic String) — prepends length + free space
embstr: [robj | sdshdr | string data...]raw: [robj] → [sdshdr | string data...]SDS (Simple Dynamic String):
struct sdshdr { int len; // String length int free; // Available free space char buf[]; // Character array};SDS is binary-safe (can contain null bytes), tracks its own length (O(1) STRLEN), and pre-allocates space for growth.
Q113. How does Redis handle memory defragmentation? Hard
Automatic memory defragmentation (Redis 4+, experimental):
# Configurationactivedefrag yes # Enable auto defragmentationactive-defrag-ignore-bytes 100mb # Start if RSS > allocated by 100MBactive-defrag-threshold-lower 10 # Start if fragmentation > 10%active-defrag-threshold-upper 100 # Stop if fragmentation > 100%active-defrag-cycle-min 5% # Minimum CPU time spentactive-defrag-cycle-max 75% # Maximum CPU time spentHow it works:
jemalloc(the default allocator) supports jemalloc arenas- Redis’s defragmentation thread moves allocations to compact them
- It runs in the background (not in the main event loop)
- The defrag thread does the work, main thread is not blocked
Manual defragmentation:
MEMORY PURGE # Ask allocator to reclaim memory (may not always help)Preventing fragmentation:
- Use consistent key sizes where possible
- Avoid frequent creation/deletion of varying-size keys
- Pre-allocate strings with
APPENDorSETRANGEduring initialization - Monitor fragmentation ratio and restart if > 1.5
Q114. What are the internals of Redis Sorted Sets (Skip Lists)? Hard
Redis Sorted Sets use two data structures internally:
1. Skip List (primary structure):
- A probabilistic data structure — O(log N) average for insert/delete/search
- Multiple layers of linked lists, each layer skipping more elements
- The bottom layer contains all elements in sorted order
- Higher layers act as “express lanes”
Skip List (conceptual):Level 3: 1 -------------------------→ 9Level 2: 1 -----------→ 5 ---------→ 9Level 1: 1 ---→ 3 ---→ 5 ---→ 7 ---→ 9Level 0: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → 92. Hash Table (complementary structure):
- Maps member → score for O(1) score lookups
- Used by
ZSCOREcommand - Kept in sync with the skip list
Why skip list instead of balanced tree?
- Simpler to implement
- No rebalancing required
- Good performance with concurrent modifications (not that Redis needs it — single-threaded!)
- Average O(log N) performance for all operations
- Range queries (ZRANGE, ZREVRANGE) are efficient
Small Sorted Sets (≤ 128 entries, entries ≤ 64 bytes) use ziplist encoding instead — no skip list overhead. Use OBJECT ENCODING key to check.
Q115. How does Redis handle cross-slot operations in Cluster mode? Hard
Limitation: Redis Cluster does NOT allow operations involving keys in different hash slots (unless they share a hash tag).
# These work (same slot with hash tag)MSET {user:1001}:name "Alice" {user:1001}:email "alice@example.com"MGET {user:1001}:name {user:1001}:email
# These fail with CROSSSLOT error (different slots)MSET user:1001:name "Alice" user:1002:name "Bob"# (error) CROSSSLOT keys in request don't hash to the same slotWorkarounds for cross-slot operations:
1. Hash tags — Force keys to the same slot:
{user:1001}:name # All these share slot based on "user:1001"{user:1001}:email2. Multi-key operations via Lua scripts — Can access keys in different slots IF they use hash tags:
-- Still limited: KEYS must share the same hash slot in ClusterEVAL "..." 2 {user:1001}:name {user:1001}:email3. Client-side multi-get — Send individual commands to the right nodes:
// Client calculates which node has which keyconst node1Keys = ['user:1001:name', 'user:1001:email']; // Node Aconst node2Keys = ['user:2002:name']; // Node B
// Parallel requests to each nodeconst [result1, result2] = await Promise.all([ node1.mget(node1Keys), node2.get(node2Keys)]);4. Redis Cluster proxy — Some proxies (like redis-cluster-proxy or Envoy) can handle cross-slot operations transparently.
Q116. What is the consistency model of Redis? Hard
Redis provides eventual consistency with weak consistency guarantees:
Single instance:
- Strong consistency within a single Redis instance (single-threaded, sequential execution)
- All clients see operations in the same order (total order)
With replication (default async):
- Eventual consistency — replicas converge to the same state over time
- Primary acknowledges writes before replicas receive them
- If primary crashes, recently acknowledged writes may be lost
With WAIT command:
- Per-write synchronization —
WAITblocks until replicas confirm - Still not fully synchronous (can timeout, partial acknowledgment)
With Sentinel/Cluster failover:
- No strong consistency during failover:
- Split-brain possible (two primaries briefly)
- Writes to the old primary after a partition may be lost
- Asynchronous replication means the promoted replica may miss the latest writes
Redis trade-off:
Consistency ← weak Redis (AP system in CAP)Availability ← strong Redis (always available for reads/writes)Partition tolerance ← strong (Cluster handles partitions)Best practices:
- Use
WAITfor critical writes - Understand that Redis is not a strongly consistent database
- Design applications assuming potential data loss during failures
Q117. How does Redis handle split-brain scenarios? Hard
Split-brain occurs when a network partition causes two nodes to believe they are the primary.
In Redis Sentinel:
- Sentinel uses quorum and majority to prevent split-brain
- A primary is only failed over if
quorumSentinels agree it’s down - If the original primary can’t communicate with the majority of Sentinels, it won’t accept writes (if
min-replicas-to-writeis configured) - After failover, the old primary becomes a replica (but partition may prevent this)
Prevention with min-replicas-to-write:
# In redis.confmin-replicas-to-write 1 # Stop accepting writes if < 1 replica connectedmin-replicas-max-lag 10 # Replica must be within 10 seconds of primaryThis ensures that during a partition, a primary isolated from its replicas stops accepting writes, reducing the chance of split-brain.
In Redis Cluster:
- Cluster uses gossip protocol with node timeout for failure detection
- A partition causes some nodes to be unreachable
- Nodes from the majority partition continue operating
- Nodes in the minority partition stop accepting writes
- Automatic failover only occurs if the replica is from the majority partition
Post-split-brain recovery:
- Redis has no automatic conflict resolution
- Manual intervention may be needed to pick the “winning” dataset
- Using
REPLICAOF NO ONEandREPLICAOFcommands
Q118. How do you benchmark Redis performance? Hard
Redis includes the redis-benchmark tool:
# Basic benchmark (100k requests, 50 parallel connections)redis-benchmark -h 127.0.0.1 -p 6379 -n 100000 -c 50
# Test specific commandsredis-benchmark -t SET,GET -n 100000
# Test with pipeliningredis-benchmark -t SET,GET -P 16 # Pipeline 16 commands
# Test with 1KB payloadsredis-benchmark -t SET -d 1000
# Test with range of loadsredis-benchmark -n 10000 -c 10 -c 50 -c 100 -c 500
# Single-threaded benchmark (no parallelism)redis-benchmark -t SET -c 1Sample output:
====== SET ====== 100000 requests completed in 1.24 seconds 50 parallel clients 3 bytes payload keep alive: 1
99.68% <= 1 milliseconds80645.16 requests per secondKey metrics:
- Requests per second — throughput (higher is better)
- Latency percentiles — P50, P99, P999 (lower is better)
- Pipeline factor — how much pipelining improves throughput
Real-world benchmarking tips:
- Test with production-like data sizes and patterns
- Test from a separate machine (not localhost)
- Test with your actual client library (differences matter)
- Test network latency impact with
redis-cli --latency - Monitor server resources during benchmark (
redis-cli INFO stats)
Q119. What is Redis pipelining and how do you calculate the optimal pipeline size? Hard
Pipeline performance depends on network latency and command count:
Calculation:
Without pipeline: Total = N × RTTWith pipeline: Total = RTT + (N / throughput)
Where:- RTT = network round-trip time (e.g., 1ms for localhost, 50ms for cloud)- N = number of commands- throughput = Redis's max ops/sec (~100k for localhost)Example (1000 commands, localhost RTT = 1ms, throughput = 100k/s):
- Without pipeline: 1000 × 1ms = 1000ms
- With pipeline: 1ms + (1000 / 100000) = 11ms (90x faster!)
Optimal pipeline size:
- Too small (< 10): Doesn’t maximize throughput
- Too large (> 1000): Consumes client memory waiting for all responses
- Recommended: 50-100 commands per pipeline batch
async function bulkInsert(data) { const pipeline = redis.pipeline(); for (const item of data) { pipeline.set(`key:${item.id}`, JSON.stringify(item)); } const results = await pipeline.exec(); // Sends all at once return results;}
// For very large datasets, batch in chunks of 100async function bulkInsertBatched(data) { for (let i = 0; i < data.length; i += 100) { const batch = data.slice(i, i + 100); const pipeline = redis.pipeline(); batch.forEach(item => pipeline.set(`key:${item.id}`, JSON.stringify(item))); await pipeline.exec(); }}Q120. How does Redis Cluster handle resharding? Hard
Redis Cluster supports online resharding (moving hash slots between nodes) without downtime:
# Start reshardingredis-cli --cluster reshard host:port
# Reshard optionsredis-cli --cluster reshard 127.0.0.1:7000 \ --cluster-from <node-id> \ --cluster-to <node-id> \ --cluster-slots 1000 \ --cluster-yesHow resharding works internally:
- Slot planning — Administrator decides how many slots to move and where
- Source node — Marks the slot as MIGRATING (still accepts reads/writes)
- Target node — Marks the slot as IMPORTING (not yet serving)
- Key migration — For each key in the slot:
MIGRATEcommand moves the key atomically- Source keeps the key (but marks it as “moved”)
- Client redirects — After migration:
- Source returns
ASKredirect to clients querying the slot - Clients use
ASKINGcommand before accessing the target
- Source returns
- Slot assignment — Once all keys are moved, the slot is reassigned to the target
- Gossip propagation — Slot map updates propagate via gossip protocol
Zero-downtime resharding:
- Reads/writes continue during the process
ASKredirects are transparent to cluster-aware clients- Multi-key operations may temporarily fail if keys from a slot being migrated are involved
Q121. How does Redis Cluster handle network partitions (split-brain)? Hard
Redis Cluster handles network partitions using the Node Timeout mechanism:
Scenario: 6-node cluster (3 primaries, 3 replicas), partition splits into two groups:
Majority Partition (e.g., 4 nodes):
- Cluster continues operating normally
- Detects minority nodes as failed
- Promotes replicas if primaries are in minority partition
Minority Partition (e.g., 2 nodes):
- Nodes can’t reach the majority
- After
node-timeout, the cluster is marked as FAIL - Primaries in the minority partition stop accepting writes
- This prevents split-brain writes
Configuration:
cluster-node-timeout 15000 # 15 seconds node timeoutcluster-slave-validity-factor 10 # Replica validity factorWhat happens:
- Partition occurs at T=0
- At T=15s (node-timeout), minority nodes mark majority as FAIL
- Minority primaries reject writes with
CLUSTERDOWNerror - Majority partition continues, promoting replicas as needed
- When partition heals, minority nodes reconnect and synchronize
- Minority nodes that accepted writes are rolled back (they didn’t accept any, because writes were rejected)
Key safety: Cluster sacrifices availability during partitions to prevent data inconsistency. This makes Redis Cluster a CP system during partitions (consistent but unavailable in the minority partition).
Q122. How does Redis implement transactions? Why no rollback? Hard
Redis transaction implementation:
MULTI → commands queued → EXECWhy no rollback:
Redis transactions differ from SQL transactions. The rationale:
-
Redis commands rarely fail at runtime — Syntax errors are caught at queue time (MULTI + command + EXEC fails the whole batch). Runtime errors are rare (e.g., type mismatch).
-
Rollback would require undo logic — Tracking every modification to revert it adds complexity and overhead that goes against Redis’s simplicity philosophy.
-
Lua scripts are the preferred alternative — For complex operations, use Lua. If you need rollback, implement it in the script.
What errors CAN happen at runtime (no rollback):
MULTISET key "hello"LPUSH key "world" # Runtime error! Cannot LPUSH a stringSET other "data"EXEC# Result: SET succeeded, LPUSH failed, SET succeededBehavior:
- If EXEC itself fails (e.g., OOM or replication issues): nothing executes
- If a queued command fails (e.g., type mismatch): only that command fails, others succeed
- There is NO transaction rollback
Best practices:
- Validate data types before transactions
- Use Lua scripts for complex multi-step operations
- Use WATCH + retry pattern for read-modify-write scenarios
Q123. How does Redis handle large binary data (e.g., images, files)? Hard
Redis Strings can hold up to 512 MB — theoretically you can store binary data, but:
Best practices for large values:
1. Don’t store files > 100KB in Redis directly
- Large values consume significant memory bandwidth
- Slow to serialize/deserialize
- Slow to replicate across replicas
- Can block the event loop (one large GET takes microseconds longer)
2. Store metadata in Redis, content in blob storage
# Redis: store reference to S3/file pathHSET file:abc123 \ name "photo.jpg" \ bucket "my-bucket" \ key "uploads/photo.jpg" \ size 5242880 \ content_type "image/jpeg"
# Content stored in S3, GCS, or local filesystem3. Chunk large data across multiple keys
# Split a large value into 1MB chunksSET file:abc123:chunk:0 <1MB of data>SET file:abc123:chunk:1 <1MB of data># ... retrieve with MGET and reassemble client-side4. Use SETRANGE and GETRANGE for stream processing
# Append chunks without loading the entire valueSETRANGE file:abc123 0 <first chunk>SETRANGE file:abc123 1048576 <second chunk>Why not to store large values:
- Doubles memory temporarily during replication (parent and child during BGSAVE)
- Slows down RDB snapshots (fork + copy-on-write for large pages)
- Slows down AOF rewrites
- Cluster rebalancing is slower
- Network bandwidth becomes bottleneck
Q124. What is Redis ACL (Access Control List) and how does it work? Hard
ACL (Access Control List) was introduced in Redis 6 for granular user permissions:
# Create a user with passwordACL SETUSER alice on >alice_password
# Grant specific command accessACL SETUSER alice +GET +SET +MGET
# Grant command category accessACL SETUSER alice +@read +@write
# Restrict key access to a patternACL SETUSER alice ~users:* ~cache:*
# Deny specific dangerous commandsACL SETUSER alice -FLUSHALL -FLUSHDB -KEYS -CONFIG
# Create a read-only userACL SETUSER readonly on >readonly_pass +@read ~*
# Create a user with all permissions except adminACL SETUSER developer on >dev_pass +@all -@dangerous ~*
# List usersACL LIST
# Get user infoACL GETUSER alice
# Test authenticationAUTH alice alice_passwordDefault user:
- The default user (
default) has all permissions - In Redis 6,
protected-modeand requiring password for the default user is recommended
ACL Categories:
| Category | Commands |
|---|---|
@read | GET, LRANGE, SMEMBERS, etc. |
@write | SET, LPUSH, SADD, etc. |
@admin | CONFIG, FLUSHALL, SHUTDOWN, etc. |
@dangerous | FLUSHALL, DEBUG, SHUTDOWN |
@fast | O(1) commands |
@slow | O(N) or heavier commands |
@connection | AUTH, PING, SELECT, QUIT |
ACL persistence:
ACL rules are persisted in acl.conf (separate from redis.conf). Use ACL SAVE to persist manually.
Q125. How does Redis Cluster's gossip protocol work? Hard
Redis Cluster nodes communicate using a gossip protocol on the cluster bus port (base port + 10000):
Gossip message content: Each node periodically sends:
- Its own information (node ID, IP, port, role, slot range, etc.)
- A sample of other nodes it knows about (2-3 nodes per message)
- PFAIL/FAIL flags for suspected/failed nodes
Gossip interval:
- PING — Every
cluster-node-timeout / 10to a random node (~1.5swith default 15s timeout) - PONG — Response to PING (also sent when a node’s state changes)
Failure detection flow:
Node A can't reach Node B ↓After timeout: Node A marks B as PFAIL (possibly failed) ↓Node A gossips B's PFAIL status to other nodes ↓If majority of primaries report B as PFAIL: → B is marked FAIL (globally) ↓Replicas of B initiate failoverGossip advantages:
- Decentralized — No single point of failure
- Scalable — Each node only communicates with a subset
- Self-healing — Nodes discover each other automatically
- Convergent — All nodes eventually learn about all changes
Gossip limitations:
- Eventual consistency — It takes time for all nodes to learn about changes
- Bandwidth overhead — Each message contains extra node information
- Messages can be large in clusters with many nodes (fixed by sampling)
Q126. What is Redis `CLUSTER` command set and what can you do with it? Hard
The CLUSTER command group manages Redis Cluster:
# Cluster informationCLUSTER INFO # Cluster state, size, slots assignedCLUSTER NODES # All nodes (ID, IP, role, slots, flags)CLUSTER SLOTS # Slot-to-node mapping (for client initialization)
# Node managementCLUSTER MEET host port # Add a node to the clusterCLUSTER FORGET node-id # Remove a nodeCLUSTER REPLICATE node-id # Make current node a replica of node-idCLUSTER REPLICAS node-id # List replicas of a nodeCLUSTER COUNT-FAILURE-REPORTS node-id # Failure reports count
# Slot managementCLUSTER ADDSLOTS slot [slot ...] # Assign slots to current nodeCLUSTER DELSLOTS slot [slot ...] # Remove slots from current nodeCLUSTER SETSLOT slot NODE node-id # Assign a slot to a nodeCLUSTER SETSLOT slot MIGRATING node-id # Mark slot as migratingCLUSTER SETSLOT slot IMPORTING node-id # Mark slot as importingCLUSTER SETSLOT slot STABLE # Cancel migration/importCLUSTER KEYSLOT key # Get hash slot for a keyCLUSTER COUNTKEYSINSLOT slot # Count keys in a slotCLUSTER GETKEYSINSLOT slot count # Get keys in a slot
# FailoverCLUSTER FAILOVER [FORCE|TAKEOVER] # Trigger manual failoverCLUSTER SET-CONFIG-EPOCH epoch # Set config epoch (bootstrap)
# Node resetCLUSTER RESET [HARD|SOFT] # Reset node (for re-clustering)Example: Query which slot a key belongs to:
CLUSTER KEYSLOT user:1001# (integer) 14523Q127. What are the limitations of Redis Cluster? Hard
1. Multi-key operations limited to same hash slot
MGET,MSET, transactions, Lua scripts with multiple keys only work if keys share a slot- Solution: Use hash tags
{key}, or perform client-side multi-node commands
2. Single database
- Only database 0 is available (SELECT is not supported)
- No namespace isolation within a single cluster
3. No cross-slot transactions
MULTI/EXECwith keys in different slots fails- Lua scripts with
KEYSin different slots fail
4. Client must be cluster-aware
- Clients must handle
MOVEDandASKredirections - Client must maintain slot-to-node mapping (or use a proxy)
- Not all Redis clients support Redis Cluster
5. Larger minimum node count for HA
- Minimum 3 nodes for basic cluster, 6 for high availability (3 primaries + 3 replicas)
6. Increased latency
- Multi-hop queries (client → wrong node → redirected → correct node)
- Gossip protocol bandwidth overhead
7. No guarantee of strong consistency during failover
- Asynchronous replication means promoted replica may miss writes
- Writes to the old primary after partition may be lost
8. Limited publisher/subscriber
PUBLISHis forwarded to all nodes, but subscribers on any node can receive messages- Works but not as efficient as single-instance
9. No automatic rebalancing
- Resharding is manual (redis-cli —cluster reshard)
- Hash tags can create hot spots if not designed carefully
Q128. How do you troubleshoot Redis latency spikes? Hard
Step-by-step troubleshooting approach:
1. Check if it’s the network
# From application serverredis-cli --latency -h <redis-host> -p <redis-port># min: 0, max: 5, avg: 0.87 (milliseconds)redis-cli --latency-history -h <redis-host> -p <redis-port> -i 52. Check for slow commands
SLOWLOG GET 203. Check for fork() delays
# Check last fork timeINFO persistence# latest_fork_usec: 15234 # 15ms fork — if > 100ms, may cause latency4. Check if AOF fsync is causing delays
INFO persistence# aof_delayed_fsync: 0 # If > 0, fsync is struggling5. Check for swap usage
INFO memory# used_memory_swap: 0 # If > 0, Redis is swapping — BAD6. Check for eviction
INFO stats# evicted_keys: 1500 # High eviction causes latency7. Check fork child progress
INFO persistence# bgsave_in_progress: 1 # BGSAVE running — may cause latency8. Use LATENCY DOCTOR
LATENCY DOCTOR# Provides automated diagnosis9. Common causes:
- Fork:
BGSAVEorBGREWRITEAOFcan cause 10-100ms pauses (fork copies page tables) - Transparent Huge Pages (THP): Disable THP on Linux (
echo never > /sys/kernel/mm/transparent_hugepage/enabled) - AOF fsync with
always: Slow on spinning disks - Swap: If Redis touches memory in swap, it pauses
- Network: Congestion, packet loss, bandwidth saturation
- Slow commands:
KEYS *,SMEMBERSon large sets,SORTon large lists
Q129. What is Redis Stack? Hard
Redis Stack is a suite of Redis modules providing additional data structures and capabilities:
# Install Redis Stack (includes all modules)# Or add modules to existing RedisModules included:
1. RediSearch — Full-text search and secondary indexing
FT.CREATE idx ON HASH PREFIX 1 doc: SCHEMA title TEXT WEIGHT 5.0 body TEXTFT.SEARCH idx "hello world" LIMIT 0 10FT.AGGREGATE idx "*" GROUPBY 1 @category REDUCE COUNT 0 AS num2. RedisJSON — Native JSON document storage
JSON.SET doc:1 $ '{"name":"Alice","age":30,"address":{"city":"NYC"}}'JSON.GET doc:1 $.address.city # "NYC"JSON.ARRAPPEND doc:1 $.tags "redis"3. RedisTimeSeries — Time-series data (IoT, finance, metrics)
TS.CREATE sensor:temp RETENTION 86400000TS.ADD sensor:temp 1700000000 25.5TS.RANGE sensor:temp 1700000000 1700001000 AGGREGATION avg 600004. RedisBloom — Probabilistic data structures (Bloom filters, Cuckoo filters, Count-Min Sketch, Top-K)
BF.ADD bloomfilter user:1001BF.EXISTS bloomfilter user:1002 # Might return false positiveCMS.INCRBY counters user:1001 1TOPK.ADD trending "topic1" "topic2"Use cases:
- Full-text search without Elasticsearch
- JSON document store (like MongoDB basics)
- Time-series data (IoT sensor data, financial data)
- Probabilistic data structures (deduplication, trending topics)
Q130. How does RediSearch work under the hood? Hard
RediSearch provides secondary indexing and full-text search on top of Redis Hashes:
Indexing:
- You define a schema — which fields to index and how (TEXT, TAG, NUMERIC, GEO)
- RediSearch maintains inverted indexes for TEXT fields
- TAG fields get simple indexes (exact match, no stemming)
- NUMERIC and GEO fields get range-indexed structures
Search process:
# Define indexFT.CREATE products_idx ON HASH PREFIX 1 product: \ SCHEMA name TEXT WEIGHT 5.0 \ description TEXT \ price NUMERIC \ category TAG \ location GEO
# SearchFT.SEARCH products_idx "wireless mouse" \ FILTER price 10 100 \ SORTBY price DESC \ LIMIT 0 10Internal architecture:
- Inverted index — Maps terms to document IDs (like Lucene)
- Trie (prefix tree) — For fast prefix searches with
*wildcard - Numeric ranges — Tree-based structure for numeric filters
- Auto-complete — Sorted suggestions with scores
Performance:
- Indexing speed: ~2M documents/sec (on modern hardware)
- Search latency: single-digit milliseconds for typical queries
- Memory overhead: ~1.5x-2x the data size for the index
Use cases:
- Product catalog search
- Log analysis
- Auto-complete / suggest
- Geo-search within certain bounds
Q131. What is the difference between `SAVE` and `BGSAVE`? Hard
Both create RDB snapshots, but differently:
| Feature | SAVE | BGSAVE |
|---|---|---|
| Blocking | ✅ Blocks Redis until complete | ❌ Background fork, non-blocking |
| Method | Direct synchronous save | Fork + child process save |
| Memory | No extra memory | copy-on-write (may use extra memory) |
| Speed | Fast (no fork overhead) | Fork takes time (usually 10-100ms) |
| Use case | Emergency shutdown, low-RAM scenarios | Normal scheduled backups |
SAVE # Redis is completely blocked until doneBGSAVE # Returns "Background saving started"
INFO persistence# rdb_bgsave_in_progress: 1# rdb_last_bgsave_status: okWhen SAVE is used automatically:
- When Redis is shut down (
SHUTDOWNcommand performs aSAVEif AOF is not enabled) - When
save ""is not configured (default config has save points)
When BGSAVE is triggered:
- Automatic save points (e.g.,
save 900 1) - Manual
BGSAVEcommand - Before
REPLICAOFsync (diskless or disk-based)
Fork overhead:
- Fork copies the page table (not the data itself)
- Time is proportional to memory size (not data size)
- Example: A 10GB Redis instance may take ~50-200ms to fork
- This is a common source of latency spikes (mitigated by: disable THP, use enough CPU, schedule BGSAVE during low traffic)
Q132. How does copy-on-write (COW) work during Redis BGSAVE? Hard
When BGSAVE (or BGREWRITEAOF) is triggered, Redis calls fork():
Copy-on-Write (COW) mechanism:
-
Before fork:
- Parent process has all data in memory pages
-
During fork:
- OS creates a child process
- Child shares the same memory pages with parent (no copy yet)
- The page table is copied (sized to virtual memory usage, not data size)
-
After fork:
- Child starts writing data to disk (RDB file or AOF temp file)
- Parent continues serving requests
-
When parent modifies data:
- OS detects a write to a shared page
- OS copies the page (original stays for child, parent gets a new copy)
- This is “copy-on-write” — pages are only duplicated when modified
Before fork:Parent: [page1][page2][page3][page4] → physical memory
After fork:Parent → [page1][page2][page3][page4] → physical memory (same pages)Child → [page1][page2][page3][page4] → physical memory (same pages)
After parent modifies page2:Parent → [page1][page2'][page3][page4] → physical memory (page2 copied)Child → [page1][page2][page3][page4] → physical memory (original page2)COW memory overhead:
- How much memory increases during BGSAVE depends on write rate
- High write rate → more pages modified → more memory copied
- Can cause memory usage to double during heavy writes + BGSAVE
- Monitor
INFO memory: used_memory_overhead
Best practice:
- Schedule BGSAVE during low-write periods
- Set
maxmemorylower if BGSAVE memory spikes are an issue - Consider diskless replication for replicas (no RDB on disk)
- Use enough memory headroom (20-50% free memory recommended)
Q133. How does Redis handle large List operations internally? Hard
Redis Lists are stored as quicklists — a linked list of ziplists:
Quicklist structure:
quicklist → [ziplist: 10 elements] ↔ [ziplist: 10 elements] ↔ [ziplist: 10 elements]Why quicklist?
- Linked list alone: O(1) push/pop but memory-heavy (each node has forward/back pointers)
- Ziplist alone: Memory-efficient but O(N) for insert/delete in middle
- Quicklist combines both: Memory-efficient + fast push/pop
Configuration:
list-max-ziplist-size -2 # Each ziplist's max size (negative = power of 2 KB) # -2 = 8KB per ziplist (default)list-compress-depth 0 # 0 = no compression # 1 = compress all but first/last ziplistOperations:
- LPUSH/RPUSH: O(1) — push to head/tail ziplist
- LPOP/RPOP: O(1) — pop from head/tail ziplist
- LINDEX: O(N) — traverse through ziplists to find the element
- LREM: O(N) — remove elements matching value
- LTRIM: O(N) — remove elements outside range (may delete entire ziplists)
Memory optimization:
list-compress-depth: Compresses middle ziplists (not recently accessed) using LZF compression- For queues where only ends are accessed, compression can save significant memory
OBJECT ENCODING mylist # "quicklist"Q134. What is the difference between Redis `ZRANGE`, `ZREVRANGE`, and `ZRANGEBYSCORE`? Hard
| Command | Returns | Order | By |
|---|---|---|---|
ZRANGE key start stop | Members at rank positions | Ascending score | Rank (0-based index) |
ZREVRANGE key start stop | Members at rank positions | Descending score | Rank (0-based index) |
ZRANGEBYSCORE key min max | Members with scores in range | Ascending score | Score range |
ZREVRANGEBYSCORE key max min | Members with scores in range | Descending score | Score range |
ZADD scores 10 "Alice" 20 "Bob" 30 "Charlie" 40 "Diana" 50 "Eve"
# By rank (position)ZRANGE scores 0 2 # Alice, Bob, Charlie (1st to 3rd)ZREVRANGE scores 0 2 # Eve, Diana, Charlie (top 3)
# By scoreZRANGEBYSCORE scores 20 40 # Bob, Charlie, DianaZREVRANGEBYSCORE scores 40 20 # Diana, Charlie, Bob
# With scoresZRANGE scores 0 -1 WITHSCORES # All members with scoresIn Redis 6.2+ these are unified:
ZRANGE scores 0 -1 BYSCORE # Same as ZRANGEBYSCORE (unified syntax)ZRANGE scores +inf -inf BYSCORE REV # Descending by scoreUse cases:
ZRANGE— Paginating leaderboards (page 1, page 2)ZRANGEBYSCORE— People with scores between X and YZREVRANGE— Top 10 leaderboard
Q135. How does Redis handle `BITOP` operations on Bitmaps? Hard
BITOP performs bitwise operations on Bitmap strings:
BITOP AND result bitmap1 bitmap2 # Bitwise ANDBITOP OR result bitmap1 bitmap2 # Bitwise ORBITOP XOR result bitmap1 bitmap2 # Bitwise XORBITOP NOT result bitmap1 # Bitwise NOT (single key only)Internal behavior:
- Operates on String values (treats strings as bit arrays)
- Shorter strings are treated as having zero bits beyond their length
- Result is stored in a new key (destination)
- Returns the size of the longest input string
Example — Daily active users:
# Day 1: users 1, 3, 5 activeSETBIT active:2024-01-01 1 1SETBIT active:2024-01-01 3 1SETBIT active:2024-01-01 5 1
# Day 2: users 2, 3, 4 activeSETBIT active:2024-01-02 2 1SETBIT active:2024-01-02 3 1SETBIT active:2024-01-02 4 1
# Users active both days (AND)BITOP AND both_days active:2024-01-01 active:2024-01-02BITCOUNT both_days # Returns 1 (only user 3)
# Users active on either day (OR)BITOP OR either_day active:2024-01-01 active:2024-01-02BITCOUNT either_day # Returns 5 (users 1,2,3,4,5)Performance:
BITOPis O(N) — it processes EVERY bit in the strings- For large bitmaps (millions of bits),
BITOPcan be slow - Consider using a Lua script for complex bit operations
- For millions of users, Bitmaps use very little memory (1M users = 125KB)
Q136. How does Redis handle time-series data? Hard
Native approaches (without RedisTimeSeries module):
1. Sorted Sets by timestamp:
# Store sensor readings with timestamp as scoreZADD sensor:temp 1700000000 "25.5"ZADD sensor:temp 1700000060 "25.8"ZADD sensor:temp 1700000120 "26.1"
# Query by time rangeZRANGEBYSCORE sensor:temp 1700000000 1700001000 WITHSCORES
# Get count of readingsZCARD sensor:temp2. Streams (Redis 5+):
XADD sensor:temp * sensor_id "s1" value 25.5XADD sensor:temp * sensor_id "s1" value 25.8
# Range queryXRANGE sensor:temp 1700000000000-0 1700001000000-0
# Trim to keep only last 10000 entriesXADD sensor:temp MAXLEN ~ 10000 * sensor_id "s1" value 26.13. RedisTimeSeries module (Redis Stack):
TS.CREATE sensor:temp RETENTION 86400000 # Keep 24 hoursTS.CREATE sensor:humidity RETENTION 86400000
TS.ADD sensor:temp * 25.5TS.ADD sensor:temp * 25.8
# Aggregated query (average per minute)TS.RANGE sensor:temp 1700000000 1700001000 \ AGGREGATION avg 60000
# Downsampling rulesTS.CREATERULE sensor:temp sensor:temp:1h \ AGGREGATION avg 3600000Comparison:
| Approach | Pros | Cons |
|---|---|---|
| Sorted Sets | Simple, native | No downsampling, memory-intensive |
| Streams | Append-optimized, trim support | No time-based aggregation |
| RedisTimeSeries | Downsampling, retention, aggregation, labels | Requires module |
Q137. What are Bloom Filters and how does RedisBloom implement them? Hard
Bloom Filter is a probabilistic data structure that checks if an element is definitely NOT in a set or probably in a set:
Initialize: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] (10-bit array)
Add "cat": hash1("cat")=2, hash2("cat")=5, hash3("cat")=9 [0, 0, 1, 0, 0, 1, 0, 0, 0, 1]
Add "dog": hash1("dog")=1, hash2("dog")=5, hash3("dog")=8 [0, 1, 1, 0, 0, 1, 0, 0, 1, 1]
Check "cat": hash1=2✅, hash2=5✅, hash3=9✅ → Probably presentCheck "fish": hash1=0❌ → Definitely NOT presentCheck "bird": hash1=2✅, hash2=4❌ → Definitely NOT presentFalse positive possible (all bits set by other elements). False negatives impossible.
RedisBloom:
BF.RESERVE myfilter 0.01 100000 # 1% error rate, 100k expected itemsBF.ADD myfilter "user:1001"BF.ADD myfilter "user:1002"BF.EXISTS myfilter "user:1001" # 1 (probably)BF.EXISTS myfilter "user:9999" # 0 (definitely not)
# Multiple adds at onceBF.MADD myfilter "user:1003" "user:1004"BF.MEXISTS myfilter "user:1003" "user:9999"
# Get infoBF.INFO myfilterUse cases:
- Cache deduplication — Check if we’ve cached an item before
- Web crawler — Avoid crawling the same URL twice
- Spam detection — Check if email is known spam (fast rejection)
- Analytics — Avoid counting the same user multiple times
Trade-off: Bloom filters use a fraction of the memory of a Set (e.g., 1MB for 10M items with 1% error rate vs 100+ MB for a Set). The cost is false positives.
Q138. How do you migrate data from one Redis instance to another? Hard
Several approaches depending on downtime tolerance:
1. Redis replication (minimal downtime):
# On new instance (target)SLAVEOF old-host 6379 # Start replication from old primary
# Wait for sync to complete (check INFO replication)# Then promote new instanceSLAVEOF NO ONE
# On old instance (point app to new)# App now uses new instance2. redis-cli —cluster import (for Cluster):
# Import from standalone Redis into Clusterredis-cli --cluster import <cluster-host>:<cluster-port> \ --cluster-from <source-host>:<source-port> \ --cluster-copy3. RDB file transfer (downtime required):
# On sourceBGSAVE# Wait for completion, then copy dump.rdb to target
# On target# Stop Redis, replace dump.rdb, restart Redis4. Dual-write strategy (zero downtime):
1. Write to both old and new Redis instances simultaneously2. Backfill old data from old to new3. After backfill completes, read from new instance (verify consistency)4. Switch reads to new instance exclusively5. Remove dual-write code5. Migrate + DUMP/RESTORE (for selective keys):
import redis
source = redis.Redis(host='old-host')target = redis.Redis(host='new-host')
for key in source.scan_iter("user:*"): value = source.dump(key) ttl = source.ttl(key) if ttl == -1: target.restore(key, 0, value) # No TTL else: target.restore(key, ttl * 1000, value) # With TTL6. File-based migration:
# Sourceredis-cli --rdb dump.rdb
# Transfer filescp dump.rdb user@new-host:/var/lib/redis/
# On new hostsystemctl stop rediscp /var/lib/redis/dump.rdb /var/lib/redis/ # Replacesystemctl start redisRecommendation: For production, use replication (method 1) — minimal downtime, automatic, and reliable.
Q139. How does Redis handle concurrent transactions with optimistic locking? Hard
Redis uses WATCH/MULTI/EXEC for optimistic concurrency control — no locks are held:
Scenario: Two clients transferring money from Account A to Account B
// Client 1async function transfer(from, to, amount) { while (true) { // Watch both accounts await redis.watch(`account:${from}`, `account:${to}`);
// Read current balances const fromBal = parseInt(await redis.get(`account:${from}`)) || 0; const toBal = parseInt(await redis.get(`account:${to}`)) || 0;
if (fromBal < amount) { await redis.unwatch(); throw new Error('Insufficient funds'); }
// Begin transaction const result = await redis .multi() .set(`account:${from}`, fromBal - amount) .set(`account:${to}`, toBal + amount) .exec();
if (result !== null) { // Transaction succeeded (no one modified watched keys) return; } // Transaction failed — retry from the beginning }}How it works under the hood:
- Client calls
WATCH key1 key2 - Redis stores a list of watched keys in the client connection state
- Before executing
EXEC, Redis checks if ANY watched key was modified (by any client) since theWATCH - If modified →
EXECreturnsnil→ application retries - If not modified →
EXECruns all commands atomically
Key points:
- No locks held — other clients can still read/write
- Optimistic — assumes conflicts are rare
- Retry loop — application must handle failure and retry
- UNWATCH — Cancel watch without executing (e.g., when you decide not to proceed)
- Best for low-contention scenarios — if many clients fight for the same keys, retries increase
Q140. What is Redis `CLIENT CACHING` and how does server-assisted client caching work? Hard
Server-assisted client caching (Redis 6+, RESP3 protocol) allows Redis to invalidate cached data on the client side:
How it works:
- Client registers interest in specific keys
- Redis tracks which keys each client is interested in
- When a key is modified, Redis sends an invalidation message to the client
- Client clears its local cache entry for that key
# Enable client tracking (RESP3 required)CLIENT TRACKING ON
# Client caches locally: user:1001 = {...}# When another client modifies user:1001:# Redis sends: → "invalidate" "user:1001"# Client deletes its local cached copyConnection modes:
- Default (broadcast mode) — Redis sends invalidation to ALL clients tracking the key
- Redirect mode (with CLIENT ID) — Invalidation messages sent to a separate connection
┌─────────┐ SET user:1001 "new" ┌─────────┐│ Client A │ ────────────────────────────→│ Redis ││ (writer) │ └────┬────┘└──────────┘ │ "invalidate user:1001" │ ┌────▼────┐ │ Client B │ ← Deletes local cache │ (reader) │ └─────────┘Benefits:
- Client-side caching reduces latency (no network call)
- Cache invalidation is immediate (no stale TTL)
- Reduces Redis server load
Configuration:
CLIENT TRACKING ON OPTIN # Client must opt-in per keyCLIENT CACHING YES # Mark the next command's key for trackingGET user:1001 # Key is now trackedQ141. How do you design a high-availability Redis architecture? Hard
Factors for HA Redis architecture:
1. Single instance + Sentinel (small to medium data, < 20GB)
┌──────────┐ │ Sentinel │ │ nodes │ (3 minimum) └────┬─────┘ │ App ──→ Primary ────┤ │ │ ┌─────┴─────┐ │ Replica 1 Replica 2- Sentinel handles automatic failover
- Reads from replicas; writes to primary
- Application uses Sentinel to discover the current primary
2. Redis Cluster (large data, > 20GB)
App ──→ Cluster-aware client │ ┌──────────┼──────────┐ Primary A Primary B Primary C │ │ │ Replica A' Replica B' Replica C'- Automatic sharding + failover
- Minimum: 3 primaries + 3 replicas
- No single point of failure
3. Multi-region (disaster recovery)
Region 1 (Primary) Region 2 (DR)┌─────────────────┐ ┌─────────────────┐│ Primary ← App │ ╔═══╗ │ Replica (RO) ││ Replica (RO) │ ║WAN║ │ (async replication)│ Sentinel │ ╚═══╝ │ Sentinel │└─────────────────┘ └─────────────────┘- Async replication across regions (using
REPLICAOF) - DR region is read-only until failover
- Can use Sentinel in each region
4. Proxy layer (for advanced routing)
App → Proxy (twemproxy/HAProxy/Envoy) │ ┌─────┴─────┐ Primary 1 Primary 2 │ │ Replica 1 Replica 2- Proxies handle connection management, read/write splitting
- Simplified client configuration (proxy handles failover)
- Envoy can handle advanced routing with Redis Cluster
Key decisions:
| Requirement | Choose |
|---|---|
| < 20GB, need HA | Sentinel |
| > 20GB, need scale | Cluster |
| Multi-region DR | Sentinel + WAN replication |
| Simple client setup | Proxy (twemproxy/Envoy) |
| Auto-discovery | Sentinel + client library |
Q142. How does Redis handle encryption in transit? Hard
Redis supports TLS (Transport Layer Security) for encryption in transit:
Configuration (redis.conf):
# Enable TLStls-port 6380 # TLS port (separate from plain 6379)port 0 # Disable plain port (TLS only)
# Certificate configurationtls-cert-file /etc/redis/redis.crttls-key-file /etc/redis/redis.keytls-ca-cert-file /etc/redis/ca.crt
# Client certificate verificationtls-auth-clients yes # Require client certstls-auth-clients optional # Client certs optional
# Replication TLStls-replication yes # Encrypt replication traffictls-cluster yes # Encrypt cluster bus traffic
# Protocol versiontls-protocols "TLSv1.2 TLSv1.3"Client connection with TLS:
redis-cli --tls \ --cert /path/to/client.crt \ --key /path/to/client.key \ --cacert /path/to/ca.crt \ -h myredis.example.com -p 6380Node.js (ioredis):
new Redis({ host: 'myredis.example.com', port: 6380, tls: { key: fs.readFileSync('./client.key'), cert: fs.readFileSync('./client.crt'), ca: [fs.readFileSync('./ca.crt')] }});Performance impact:
- TLS adds ~10-20% CPU overhead (more CPU-intensive)
- TLS 1.3 is faster than TLS 1.2 (fewer round trips)
- Hardware acceleration (AES-NI, etc.) helps significantly
Alternatives:
- Stunnel — Wrapper for Redis without built-in TLS
- VPN — Encrypt traffic between Redis nodes
- Kubernetes — Service mesh (Istio/Linkerd) for mTLS
Q143. What is Redis `CLUSTER FAILOVER TAKEOVER` and when would you use it? Hard
CLUSTER FAILOVER TAKEOVER forces a replica to become primary without consent from other nodes:
# On a replica nodeCLUSTER FAILOVER TAKEOVERHow it’s different from regular failover:
| Feature | Normal Failover | FORCE | TAKEOVER |
|---|---|---|---|
| Majority consent | Required (cluster majority) | Required | Not required |
| Primary availability | Not needed (assumed down) | Not needed | Not needed |
| Data freshness check | Yes (replication offset) | Skipped (manual override) | Skipped |
| Cluster rejoin | Replica syncs new data | May lose writes | May lose writes |
When to use TAKEOVER:
- Emergency recovery — When cluster majority is lost and data availability is critical
- Manual cluster repair — When a primary fails and no automatic failover triggers
- Testing — Simulating cluster failure scenarios
Risks:
- Data loss — The replica may not have the latest data
- Split-brain — Can create multiple primaries for the same slot
- Cluster instability — Other nodes may be confused by the forced promotion
Safer alternative: CLUSTER FAILOVER FORCE — This doesn’t require primary consent but still requires cluster majority.
Best practice:
- Use
TAKEOVERonly as a last resort in disaster recovery - Document and plan for the data loss implications
- Monitor cluster health after forced failover
Q144. How do you handle Redis security in production? Hard
Multi-layered security approach:
1. Network security
# Bind to specific interfaces (not 0.0.0.0)bind 127.0.0.1 192.168.1.100
# Disable dangerous commands or rename themrename-command FLUSHALL ""rename-command FLUSHDB ""rename-command CONFIG "MYPRIVATE_CONFIG"rename-command DEBUG ""rename-command KEYS "SEARCH_KEYS"
# Protected mode (enabled by default)protected-mode yes# Only accepts connections from localhost if no password set2. Authentication
# Redis 6+ ACL (recommended)aclfile /etc/redis/users.acl
# Redis < 6 (legacy)requirepass YourStrongPassword123!3. TLS encryption (encryption in transit)
tls-port 6380port 0 # Disable plain TCPtls-cert-file /etc/redis/redis.crttls-key-file /etc/redis/redis.keytls-ca-cert-file /etc/redis/ca.crttls-auth-clients yes4. Operating system security
# Run as non-root useruseradd -r redischown -R redis:redis /var/lib/redischmod 750 /var/lib/redis
# Linux kernel hardeningecho 'never' > /sys/kernel/mm/transparent_hugepage/enabled# THP can cause latency spikes
# vm.overcommit_memory = 1# Required for BGSAVE fork to succeed5. Firewall rules
# Only allow application serversiptables -A INPUT -p tcp --dport 6379 -s 10.0.0.0/8 -j ACCEPTiptables -A INPUT -p tcp --dport 6379 -j DROP6. Monitoring and auditing
- Monitor for unauthorized access attempts
- Redis
INFOcommands count failed auths - Log all commands with
AUDIT-LOGmodule (or application-level logging)
7. Docker security
# Don't expose Redis ports to the internet unnecessarily# Use Docker internal network for Redisdocker run --network app_network redis:7-alpineQ145. How does Redis handle AOF file corruption and recovery? Hard
AOF file corruption can happen due to:
- Disk failure or bad sectors
- System crash during AOF write
- Insufficient disk space during append
Redis recovery tools:
1. redis-check-aof (built-in)
# Fix AOF file (removes corrupted tail)redis-check-aof --fix appendonly.aof
# Output:# AOF analyzed: size=1234567, ok_up_to=1234000# Truncating AOF to offset 1234000# AOF loaded back to memory... done!What it does:
- Scans the AOF file for valid commands
- Truncates at the last valid command before corruption
- Drops trailing corrupted data
- Redis can then restart with the repaired AOF
2. Redis configuration for partial corruption:
# In redis.confaof-load-truncated yes # Load truncated AOF (warn, then proceed)When aof-load-truncated is yes:
- Redis logs a warning about truncation
- Loads the AOF up to the last valid command
- Continues normal operation
- You should run
redis-check-aof --fixto permanently repair
When aof-load-truncated is no:
- Redis refuses to start if AOF is truncated
- You must manually fix the AOF first
3. AOF + RDB hybrid (Redis 4+): In hybrid persistence:
- AOF starts with an RDB base (less replay)
- AOF corruption recovery is faster (RDB part is already a snapshot)
4. Prevention:
- Use
appendfsync everysec(good balance) - Monitor disk space and I/O
- Use reliable storage (SSD, cloud block storage)
- Regular backups of AOF files
Q146. How does Redis handle the `SORT` command internally? Hard
The SORT command sorts elements of a List, Set, or Sorted Set by value or by external keys:
SORT mylist [BY pattern] [LIMIT offset count] [GET patterns] [ASC|DESC] [ALPHA] [STORE dest]Internal implementation:
- Load all elements into an array
- If
BY patternis specified:- For each element, construct a key using the pattern
GETthe value of that key- Use that value as the sort key
- Sort the array using quicksort (average O(N log N))
- Apply
LIMITto trim results - Process
GETpatterns (fetch additional related keys) - If
STOREis specified, store sorted results in a List
Memory implications:
SORTcreates a temporary array with all elements and their sort keys- For large datasets, this can use significant memory
- The temporary array is allocated on the heap
Performance:
- O(N + M log M) where N = number of elements, M = number to sort
- For a List with 100K elements,
SORTcan take 10-100ms - For a List with 1M elements, it can take hundreds of milliseconds
Considerations for large datasets:
# Slow — sorts ALL 1M elementsSORT biglist BY user:*->name
# Faster — limit to top 100SORT biglist BY user:*->name LIMIT 0 100Better alternatives:
- Sorted Sets — If you sort by score, use ZRANGE
- Client-side sorting — For small datasets, sort in the application
- Pre-sorted data — Store data in the order you need
Note:
SORTis being deprecated in Redis 7+ in favor ofSORT_RO(read-only variant that doesn’t block writes).
Q147. How does Redis 7's `ACL V2` differ from Redis 6 ACLs? Hard
Redis 7 introduced significant ACL improvements over Redis 6:
Redis 6 ACL:
- Basic user/command/key permissions
- Categories (e.g., +@read, +@write)
- Selector-based permissions (per-command)
Redis 7 ACL V2 additions:
1. Selectors — Multiple permission sets per user
ACL SETUSER developer ~app:* +GET +SET # Default permissions selectors +~admin:* +FLUSHALL # Additional permission set (OR logic)A user matches a command if it’s allowed by ANY selector (OR across selectors).
2. Key patterns with % for write-specific access
# Can read all keys, but only write to session:*ACL SETUSER worker ~* %R~* %W~session:* +@all3. Permission logging and resolution
ACL LOG # See ACL denial eventsACL DRYRUN user command [args] # Test if a user can run a command4. Pub/Sub channel restrictions
ACL SETUSER subscriber &chat:* -@all +SUBSCRIBE +PSUBSCRIBE# & restricts which Pub/Sub channels the user can access5. Selector-based key permissions with RESET and SEL
ACL SETUSER worker ~cache:* +GET +SET # Read/write cache:* SEL # New selector ~temp:* +SET -GET # Write-only temp:*Upgrade notes:
- Redis 6 ACLs work in Redis 7 without changes
- New features (selectors, channel restrictions) are Redis 7+
- Use
ACL GETUSERto see the complete user definition including selectors
Q148. How does Redis handle diskless replication? Hard
Diskless replication (Redis 2.8.18+) sends the RDB directly to replicas over the network without writing to disk first:
# In redis.confrepl-diskless-sync yes # Enable diskless replicationrepl-diskless-sync-delay 5 # Wait 5 seconds for more replicas to connectrepl-diskless-load onelines # Load RDB from socket on replica sideHow it works:
Traditional (disk-based) replication:1. Primary forks BGSAVE → writes RDB to disk2. RDB syncs to disk (fsync)3. Replica connects → reads RDB from disk → sends over networkProblem: Double I/O (write + read) + disk space for RDB
Diskless replication:1. Primary forks BGSAVE → child writes RDB DIRECTLY to replica socket2. Replica receives RDB stream and loads directly into memoryBenefit: No disk I/O for RDB file on primary!Advantages:
- No disk overhead for RDB file (reduced I/O)
- Faster sync for replicas (no write-then-read delay)
- No disk space needed for RDB on primary
- Ideal for high-write environments where disk I/O is bottleneck
Disadvantages:
- Higher network bandwidth usage during sync
- If replication fails, must restart from scratch (no cached RDB on disk)
- Fork required (same as disk-based)
- Not available for all storage configurations
When to use:
- High write volumes where disk I/O is saturated
- SSDs with limited write endurance
- Large datasets where RDB file is very large (> 10GB)
- Replicas in the same data center (low latency, high bandwidth)
When NOT to use:
- Cross-region replication (higher chance of network failure, needs retry)
- Slow or congested network links
- Need RDB file as backup anyway
Q149. What is the difference between Redis `ZUNIONSTORE` and `ZINTERSTORE`? Hard
Both aggregate multiple Sorted Sets into a destination:
| Command | Operation | Result |
|---|---|---|
ZUNIONSTORE dest n key [key ...] | Union | All members from any set (unique, with aggregated scores) |
ZINTERSTORE dest n key [key ...] | Intersection | Members in ALL sets (with aggregated scores) |
Syntax:
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS w1 w2 ...] [AGGREGATE SUM|MIN|MAX]ZINTERSTORE destination numkeys key [key ...] [WEIGHTS w1 w2 ...] [AGGREGATE SUM|MIN|MAX]Example:
ZADD site1 100 "page:/home" 50 "page:/about" 30 "page:/contact"ZADD site2 80 "page:/home" 60 "page:/blog" 20 "page:/about"
# Total visits (union, sum scores)ZUNIONSTORE combined 2 site1 site2 AGGREGATE SUMZRANGE combined 0 -1 WITHSCORES# "page:/contact" 30# "page:/about" 70 (50 + 20)# "page:/blog" 60# "page:/home" 180 (100 + 80)
# Pages visited on BOTH sites (intersection)ZINTERSTORE both 2 site1 site2 AGGREGATE SUMZRANGE both 0 -1 WITHSCORES# "page:/home" 180# "page:/about" 70
# Weighted union (give site2 more importance)ZUNIONSTORE weighted 2 site1 site2 WEIGHTS 1 2 AGGREGATE SUM# site1: weights → page:/about = 50×1 = 50# site2: weights → page:/about = 20×2 = 40# result: page:/about = 90Performance:
- O(N) + O(M log M) where N = total elements across all input sets
- Memory: creates temporary sorted arrays
- For large datasets, can be CPU and memory intensive
Limitations:
- Number of keys (
numkeys) must be ≤ 2048 (config limit on most systems) ZINTERSTORErequires at least one key to have the member (no member in empty set = no intersection)
Q150. What is Redis `CLUSTER SETSLOT` and how do you manage slot migration? Hard
CLUSTER SETSLOT manages hash slot assignment during resharding:
States:
- NODE — Slot is assigned to a node (normal operation)
- MIGRATING — Slot is being moved FROM this node
- IMPORTING — Slot is being moved TO this node
- STABLE — Reset migration/import state
# Step 1: On source node, mark slot as migratingCLUSTER SETSLOT 1000 MIGRATING <target-node-id>
# Step 2: On target node, mark slot as importingCLUSTER SETSLOT 1000 IMPORTING <source-node-id>
# Step 3: Migrate keys (from source to target)redis-cli --cluster slot-operation <source-host>:<port> \ --cluster-from <source-id> \ --cluster-to <target-id> \ --cluster-slots 1000
# Step 4: On any node, assign slot to targetCLUSTER SETSLOT 1000 NODE <target-node-id>During migration:
- Source node handles requests for the migrating slot
- If source has the key → return it
- If source doesn’t have the key → return
ASKredirect to target - Client must send
ASKINGcommand before accessing the key on target
Atomic slot migration:
Step 1: Mark slot MIGRATING on source → Source tracks which keys have movedStep 2: For each key: MIGRATE key target-host target-port key 0 5000 → Key moves atomically from source to targetStep 3: Mark slot NODE on target → All nodes learn about the change via gossipComplete resharding example:
# Automated reshardingredis-cli --cluster reshard 127.0.0.1:7000 \ --cluster-from <all> \ --cluster-to <node-id> \ --cluster-slots 1000 \ --cluster-yes
# Check slot distributionredis-cli --cluster info 127.0.0.1:7000redis-cli --cluster check 127.0.0.1:7000Q151. How do you optimize Redis memory usage for large datasets? Hard
1. Use smaller data types where possible:
# Instead of storing many Strings → use HashSET user:1001:name "Alice" # Each key has ~50-100 bytes overheadSET user:1001:email "alice@..." # Each key: more overheadHSET user:1001 name "Alice" email "alice@..." # One key, less overhead2. Use ziplist encoding for small collections:
# In redis.conf (defaults are good, but verify)hash-max-ziplist-entries 512hash-max-ziplist-value 64set-max-intset-entries 512zset-max-ziplist-entries 128zset-max-ziplist-value 643. Enable key compression (if using modules):
- Redis doesn’t compress keys natively
- Use shorter key names (but keep them readable)
4. Use MEMORY DOCTOR for recommendations:
MEMORY DOCTOR5. Calculate key overhead:
# Check memory without keysINFO memory# used_memory_dataset: actual data size# used_memory_overhead: key metadata# Peak is good to know too6. Use integer encoding for Sets:
# All-integer Sets use intset (much more compact)SADD myset 1 2 3 4 5OBJECT ENCODING myset # "intset" (compact)SADD myset "string"OBJECT ENCODING myset # "hashtable" (18x+ more memory!)7. Tune for your access pattern:
- Many small keys → Hash (up to 5x memory reduction)
- Large values → Consider compression (client-side gzip before SET)
- Frequently accessed → Keep hot keys small
8. Set maxmemory and monitor eviction:
CONFIG SET maxmemory 8gbCONFIG SET maxmemory-policy allkeys-lruINFO stats | grep evicted_keysQ152. How does Redis handle replication when the network between primary and replica is slow? Hard
Slow network scenarios:
1. Partial resynchronization (psync2):
- Redis 4+ supports partial resync — if a replica disconnects briefly, it doesn’t need a full RDB sync
- The primary keeps a replication backlog (buffer of recent commands)
- On reconnection, the replica sends its replication ID and offset
- If the offset is still in the primary’s backlog, only the missing commands are sent
- Config:
repl-backlog-size 1mb(increase for longer disconnections)
# Check backlogINFO replication# repl_backlog_active:1# repl_backlog_size:1048576# repl_backlog_histlen:987654 # Current data in backlog2. Diskless replication:
repl-diskless-sync yes # Stream RDB directly over network (no disk I/O)repl-diskless-sync-delay 5 # Wait for more replicas3. Replication tuning:
# Reduce replication bandwidthrepl-disable-tcp-nodelay yes # Nagle's algorithm (saves bandwidth, higher latency)repl-backlog-size 10mb # Larger backlog = less full resyncrepl-timeout 120 # Higher timeout for slow linksrepl-backlog-ttl 3600 # How long to keep backlog when no replicas
# For cross-region replicationclient-output-buffer-limit replica 256mb 64mb 60# Hard limit: 256MB# Soft limit: 64MB for 60 seconds4. Monitoring slow replication:
INFO replication# master_repl_offset: 1234567# slave_repl_offset: 1234000 → 567 behind
# Check for disconnections# repl_backlog_first_byte_offset: measures how far back data is stored5. Throttling writes during replication:
# On replicareplica-serve-stale-data yes # Serve old data during resync (better UX)replica-read-only yes # Default: safeProactive measures:
- Increase
repl-backlog-sizefor networks with brief outages (5-10MB recommended) - Use
repl-backlog-ttlto keep backlog for disconnected replicas - Monitor
INFO replication: master_repl_offset - slave_repl_offsetfor lag - Add monitoring alerts when replica lag exceeds threshold (e.g., 30 seconds)
Q153. How does Redis handle `CLIENT KILL` and what different kill types exist? Hard
CLIENT KILL terminates client connections. Redis 6+ supports filtering by type:
# Kill by addressCLIENT KILL 127.0.0.1:56789
# Kill by typeCLIENT KILL TYPE normal # Kill regular clientsCLIENT KILL TYPE master # Kill primary connectionsCLIENT KILL TYPE replica # Kill replica connectionsCLIENT KILL TYPE pubsub # Kill Pub/Sub subscribers
# Kill by user (Redis 6+) — requires ACLCLIENT KILL USER alice
# Kill by ID (Redis 6+)CLIENT KILL ID 42
# Kill by skip-meCLIENT KILL SKIPME yes/no # Whether to skip killing this connection itselfKill filters in Redis 6+:
# Complex filtersCLIENT KILL ADDR 127.0.0.1:6379CLIENT KILL LADDR 127.0.0.1:6379 # Local addressCLIENT KILL TYPE normal PUBSUB # Not combined — use TYPE onlyUse cases:
- Maintenance — Disconnect idle connections before restart
- Debugging — Kill a problematic client
- Security — Kill connections from an unauthorized IP
- Resource management — Kill connections consuming too much output buffer
Monitor blocked clients:
CLIENT LIST# id=3 addr=127.0.0.1:6379 fd=8 name= age=10 idle=0 flags=N ...# Flags: N=normal, M=master, S=replica, b=blocked, O=client in MONITOR mode
# Count clientsINFO clients# connected_clients: 42# blocked_clients: 2# client_biggest_input_buf: 1024# client_biggest_output_buf: 8192Q154. How does Redis handle output buffer management for different connection types? Hard
Redis uses output buffers to hold response data before sending to clients. Different connection types have different buffer limits:
# In redis.conf
# Normal clients (regular GET/SET clients)client-output-buffer-limit normal 0 0 0# 0 = no limit (basically unlimited)
# Replica clients (replication connections)client-output-buffer-limit replica 256mb 64mb 60# Hard limit: 256MB → immediate disconnect# Soft limit: 64MB for 60 seconds → disconnect after persistent soft limit
# Pub/Sub clients (subscribers)client-output-buffer-limit pubsub 32mb 8mb 60# Hard limit: 32MB# Soft limit: 8MB for 60 secondsWhy limits matter:
Normal clients: Unlimited by default because responses are read immediately. If a client reads slowly, the buffer grows.
Replica clients: Large buffers needed for RDB sync. If a replica is slow to process, the buffer grows and can consume significant memory.
Pub/Sub clients: Publishers can produce faster than subscribers can consume. Slow subscribers cause buffer growth → memory drain.
Symptoms of buffer issues:
# Check in CLIENT LISTCLIENT LIST# ... omem=12345678 ... ← output buffer memory (too large!)CONFIG SET for runtime changes:
CONFIG SET client-output-buffer-limit "normal 0 0 0"CONFIG SET client-output-buffer-limit "replica 512mb 128mb 60"CONFIG SET client-output-buffer-limit "pubsub 64mb 16mb 60"Detecting problematic clients:
- Use
CLIENT LISTand sort byomem(output buffer memory) - Identify clients with large buffers
- Kill problematic clients with
CLIENT KILL
Best practices:
- Set realistic Pub/Sub limits (subscribers that can’t keep up should be disconnected)
- Monitor
client_biggest_output_bufinINFO clients - Monitor for slow consumers when using Pub/Sub
Q155. How do you troubleshoot "OOM command not allowed when used memory > 'maxmemory'" errors? Hard
This error means Redis has reached maxmemory AND the eviction policy is noeviction:
Immediate steps:
1. Check current memory usage:
INFO memory# used_memory_human: 1.5G# maxmemory_human: 1.5G# maxmemory_policy: noeviction2. Change eviction policy temporarily:
CONFIG SET maxmemory-policy allkeys-lru# Redis will immediately start evicting keys3. OR increase maxmemory:
CONFIG SET maxmemory 2gb4. Free memory by deleting non-essential keys:
# Scan and delete (use with caution)SCAN 0 COUNT 1000# Or flush specific databasesFLUSHDB # Current databaseFLUSHALL # All databases (CAREFUL!)5. Find large keys:
# Use redis-cli to find big keysredis-cli --bigkeys# Lists biggest String, List, Set, etc.6. Check for memory leaks or unexpected growth:
INFO stats | grep keyspace# Look for unexpected key count growthPrevention:
# 1. Set an appropriate eviction policyCONFIG SET maxmemory-policy allkeys-lru
# 2. Monitor memory usage# Set up alerts at 70%, 80%, 90% maxmemory
# 3. Plan capacity# maxmemory should be:# 50-70% of total RAM (for BGSAVE overhead)# Below system memory (prevent OOM kills)
# 4. Set up cache TTLs# All cache keys should have EXPIRE set
# 5. Use MEMORY STATS for detailed analysisMEMORY STATS
# 6. Monitor eviction rateINFO stats | grep evicted_keysDon’t do:
- ❌
CONFIG SET maxmemory 0(unlimited — can OOM the server) - ❌ Ignore the error — it will persist until fixed
- ❌ Restart Redis without fixing — the underlying cause remains
🎯 Quick Summary: These 155 questions cover Redis fundamentals, data types, persistence, replication, clustering, security, performance tuning, and advanced internals. Master these for Redis interview success.