Basic data structures
String
- Strings, integers, floating-point numbers
- Operate on an entire string or part of one; increment or decrement integers and floats
Hash
- An unordered hash table of key-value pairs
- Add, get and remove individual key-value pairs, get all key-value pairs, check whether a given key exists
List
- Linked list
- Push or pop elements from either end, read one or more elements and trim, keeping only the elements within a range
Set
- Unordered set
- Add, get and remove individual elements, check whether an element exists in the set, compute intersections, unions and differences, fetch random elements from the set
Sorted set
- Ordered set
- Add, get and delete elements, fetch elements by score range or by member, compute the rank of a key
Complex data structures
Bitmaps
- A Bitmap is not an actual data type in Redis, but a way of using a String as a bitmap. You can think of it as turning a String into an array of bits. Using a Bitmap to store simple true/false data is extremely space-efficient.
HyperLogLog
- HyperLogLogs is a data structure mainly used for cardinality counting. It’s similar to a Set in that it maintains a collection of unique Strings, but HyperLogLogs doesn’t maintain the actual members — only the number of members. In other words, HyperLogLogs can only be used to count the number of distinct elements in a set, so it saves a lot of memory compared to a Set
Backup and disaster recovery for Redis outside distributed scenarios
Option 1
- One Master node and two Slave nodes. When the client writes data it writes to the Master node; when reading it reads from the two Slaves, which scales out reads and lightens the read load on the Master.
Option 2
- Master and Slave1 use keepalived for VIP failover. The client connects to the Master through the VIP. This avoids the IP-change problem of option 1.
Redis Sentinel architecture
- The Sentinel cluster monitors itself and the Redis master-slave replication. When it detects that the Master node has failed, the following steps happen:
- The Sentinels hold an election among themselves, elect a leader, and the elected leader performs the failover
- The Sentinel leader picks one of the slave nodes as the new Master node.
- The Sentinel cluster monitors itself and the Redis master-slave replication. When it detects that the Master node has failed, the following steps happen:
Client tools
Performance testing tools
Test with the default parameters
redis-benchmarkTest with custom parameters
redis-benchmark -n 1000000 --csvXueqiu rdr:
https://github.com/xueqiu/rdrredis-rdb-tools:
https://github.com/sripathikrishnan/redis-rdb-tools
Utility commands
Start the service with a specified config file
redis-server redis.confStart the service on a specified port
redis-server --port 6379
Tool for checking and repairing local data files
redis-check-dump dump.rdb
Tool for checking and repairing the AOF log file
redis-check-aof appendonly.aof
Basic commands
keys
- List all keys in Redis
del
- Delete one or more keys, separating multiple keys with spaces. Its return value is an integer indicating how many existing keys were deleted successfully. So if you delete only one key, you can judge from the return value whether it succeeded; if you delete multiple keys, you only get the number that were successfully deleted.
exists
- The exists command checks whether one or more keys exist; when checking multiple keys, separate them with spaces. The return value of exists is an integer indicating how many of the keys being checked currently exist.
expire/pexpire
- expire sets how many seconds until the key expires; pexpire sets how many milliseconds until the key expires. Returns 1 on success, 0 on failure.
ttl/pttl
The ttl and pttl commands get the expiration time of a key; the return value is an integer
If the key does not exist or has expired, it returns -2.
If the key exists and is permanently valid, it returns -1.
If the key has an expiration time set, it returns the remaining seconds (milliseconds for pttl)
expireat/pexpireat
- Set a key to expire at a certain timestamp. The expreat timestamp is expressed in seconds, while pexpireat uses milliseconds. Similar to expire and pexpire: returns 1 for success, 0 for failure.
persist
- Remove the key’s expiration time and make the key permanently valid. If the key had an expiration time set, persist returns 1 after removing it; if the key doesn’t exist or was already permanently valid, it returns 0
type
- Determine what type of data structure the key is. The return value is string, list, set, hash or zset, corresponding to the five basic Redis data structures described earlier.
Complex data structures such as geo, hyperloglog and bitmaps are all implemented on top of those five basic data structures — for example geo is of type zset, while hyperloglog and bitmaps are both string.
auth
- The Redis authentication command; you must authenticate before running other commands
ping
- Test connectivity between client and server; it returns PONG, meaning the connection works
config get *
- Get all configuration parameters
config set config_name config_value
- Set a configuration parameter value
info
- Return server information
select
- Switch databases; the default Redis databases are 0-15, 16 in total
move
- Move a key from the current database to another database
dbsize
- Get the number of all keys in the current database
flushdb
- Delete all keys in the current database
flushall
- Delete all keys in every database
save
- Create a backup of the current database
bgsave
- Same as save, but the backup runs in the background and doesn’t block the main process
eval
- Execute a Lua script
string
set
- Set a value for a key; you can use the EX/PX options to specify the key’s time to live
get
- Get the value corresponding to a key
getset
- Set a value for a key and return the key’s original value
incr/decr
- Increment/decrement (the key’s value must be an integer)
incrby/decrby
- Increment or decrement by a specified step (the key’s value must be an integer)
mset
- Set values for multiple keys
msetnx
- Same as MSET, but if any one of the specified keys already exists, no operation is performed
mget
- Get the values corresponding to multiple keys
strlen
- Get the length of the key
append
- Append a value to the specified key, returning the string length
setnx
- Check whether the key exists: returns 0 if it does, otherwise 1; it won’t overwrite the original value
getrange
- Get the key’s value by the specified index
list
lpush
- Insert one or more elements at the left (head) of the specified List, returning the List’s length after insertion
rpush
- Same as lpush, but inserts one or more elements at the right (tail) of the specified List
lpushx/rpushx
- Similar to lpush/rpush, except that if the key lpushx/rpushx operates on doesn’t exist, no operation is performed
lrange
- Return the elements in the specified range of the specified List (inclusive at both ends, i.e. lrange key 0 10 returns 11 elements); time complexity O(N). Try to control how many elements you fetch at a time — fetching too large a range of List elements at once causes latency, and for a List whose length is unpredictable, avoid whole-traversal operations like lrange key 0 -1
lindex
- Return the element at the specified index in the specified List; if the index is out of range, returns nil. Index values wrap around, so -1 is the last position in the List and -2 is the second-to-last position.
linsert
- Insert a new element before/after the given element in the specified List and return the List’s length after the operation. If the given element doesn’t exist, returns -1. If the specified key doesn’t exist, no operation is performed
lset
Set the element at the specified index of the specified List to value
- If the index is out of range it returns an error, time complexity O(N),
- If the operation is on the head/tail element, the time complexity is O(1)
lpop
- Remove one element from the left (head) of the specified List and return it
rpop
- Same as lpop, but removes one element from the right (tail) of the specified List and returns it
llen
- Return the length of the specified List
hash
hset
- Set field in the Hash associated with key to value. If that Hash doesn’t exist, one is created automatically.
hget
- Return the value of the field in the specified Hash
hsetnx
- Same as HSET, but if the field already exists, HSETNX performs no operation
hexists
- Check whether the field exists in the specified Hash: returns 1 if it exists, 0 if not
hincrby
- Same as the incrby command, performing incrby on a field in the specified Hash
hmset/hmget
- Same as HSET and HGET, but can batch-operate on multiple fields under the same key
hdel
- Delete the field(s) (one or more) from the specified Hash
hgetall
- Return all field-value pairs in the specified Hash. The result is an array in which field and value alternate
hkeys/hvals
- Return all fields/values in the specified Hash
hlen
- Return the number of fields in the specified hash table
set
scard
- Return the number of members in the specified Set
sismember
- Check whether the given value exists in the specified Set
smove
- Move the given member from one Set to another Set
sadd
- Add one or more members to the specified Set; if the specified Set doesn’t exist, one is created automatically.
srem
- Remove one or more members from the specified Set
srandmember
- Return one or more members at random from the specified Set
spop
- Randomly remove and return count members from the specified Set
smembers
- Return all members in the specified Hash
sunion/sunionstore
- Compute the union of multiple Sets and return it / store it in another Set
sinter/sinterstore
- Compute the intersection of multiple Sets and return it / store it in another Set
sdiff/sinterstore
- Compute the difference between one Set and one or more Sets and return it / store it in another Set
zset
- zadd
- zrem
- zcard
- zcount
- zscore
- zrank/zrevrank
- zincrby
- zrange/zrevrange
- zrangebyscore/zrevragebyscore
- zremrangebyrank/zremrangebyscore
Transactions
multi
- Start a transaction
exec
- Execute the transaction
discard
- Discard the transaction
watch
- Watch a database key; if it changes, return empty
Replication
info replication
- Get replication information
slaveof
- Establish a replication relationship
sync
- Synchronize
Publish/subscribe
subscribe
- Subscribe to one or more channels
publish
- Send a message to a channel
Performance tuning
Avoid storing bigkeys
Use pipelining to combine consecutive commands into one execution
The OS Transparent Huge Pages feature must be turned off
echo never > /sys/kernel/mm/transparent_hugepage/enabledDeploy Redis on physical machines
- When Redis persists data, it does so by creating a child process.
Creating a child process invokes the operating system’s fork system call, and how long that call takes depends on the system environment.
Fork takes much longer to run in a virtual machine than on a physical machine, so your Redis should be deployed on physical machines as much as possible
- When Redis persists data, it does so by creating a child process.
Review the data persistence strategy
Consider introducing read/write splitting
Enable the lazy-free mechanism
- If you can’t avoid storing bigkeys, then I suggest enabling Redis’s lazy-free mechanism. (Supported in 4.0+.)
Once it’s enabled, when Redis deletes a bigkey the time-consuming memory release is moved to a background thread, which avoids affecting the main thread as much as possible
- If you can’t avoid storing bigkeys, then I suggest enabling Redis’s lazy-free mechanism. (Supported in 4.0+.)
Don’t use commands with excessively high complexity
- Avoid executing aggregation-type commands such as SORT, SINTER, SINTERSTORE, ZUNIONSTORE, ZINTERSTORE and so on.
For aggregation like that, I suggest running it on the client so Redis doesn’t have to shoulder too much computation
- Avoid executing aggregation-type commands such as SORT, SINTER, SINTERSTORE, ZUNIONSTORE, ZINTERSTORE and so on.
When running O(N) commands, watch how large N is
When querying data, follow these principles
- First check the number of data elements (LLEN/HLEN/SCARD/ZCARD)
- If the element count is small, you can query all the data in one go
- If the element count is very large, query the data in batches (LRANGE/HASCAN/SSCAN/ZSCAN)
Watch the time complexity of DEL
When deleting a key, the more elements it has, the slower DEL runs
- List type: run LPOP/RPOP repeatedly until all elements are deleted
- Hash/Set/ZSet type: first run HSCAN/SSCAN/SCAN to look up the elements, then run HDEL/SREM/ZREM to delete each element in turn
Use batch commands instead of single commands
The advantage of batch operations over many single operations is that they significantly cut down the back-and-forth network I/O between client and server
For String / Hash use MGET/MSET instead of GET/SET, and HMGET/HMSET instead of HGET/HSET
- For other data types use Pipeline, packing multiple commands to send to the server in one go
Avoid keys expiring in a concentrated burst
If your business has a large number of keys expiring at the same moment, Redis cleaning up expired keys also risks blocking the main thread
When setting expiration times, add a random time so the keys’ expiry times are spread out, which reduces the impact of a concentrated expiry on the main thread
Use long connections to Redis and configure the connection pool sensibly
- Your business should use long connections to operate Redis, avoiding short connections
- With short connections, every operation goes through the TCP three-way handshake and four-way teardown, and that process adds overhead to each operation
- Your client should also access Redis through a connection pool with sensible parameters, and release connection resources promptly when Redis isn’t used for a long time
Use only db0
- When operating on data in multiple dbs over one connection, you have to run SELECT first every time, which puts extra pressure on Redis
- The purpose of multiple dbs is to store data by business line — so why not split into multiple instances instead? Deploying as multiple instances means the business lines don’t affect each other, and it also improves Redis access performance
- Redis Cluster only supports db0, so if you want to migrate to Redis Cluster later the migration cost is high
Use read/write splitting + sharded clusters
- If your business has a very large volume of read requests, you can deploy multiple replicas to achieve read/write splitting, letting the replicas share the read pressure and improving performance
- If your business has a very large volume of write requests and a single Redis instance can no longer support that much write traffic, then you need to use a sharded cluster to share the write pressure
Don’t enable AOF, or configure AOF to fsync every second
- For businesses that aren’t sensitive to data loss, I suggest not enabling AOF, to avoid AOF disk writes slowing Redis down
- If you really do need AOF enabled, I suggest configuring appendfsync everysec, which moves the persistence flush to a background thread and minimizes the impact of Redis disk writes on performance
Long-running commands
Avoid using these O(N) commands
- Don’t use List as a list; use it only as a queue
- Strictly control the size of Hash, Set and Sorted Set through mechanisms of your own
- Where possible, run sorting, union and intersection operations on the client
- Never use the keys command
- Avoid traversing all members of a collection type in one go; instead use the scan family of commands for batched, cursor-based traversal
The Slow Log feature, which automatically records long-running commands
- slowlog-log-slower-than xxxms #commands that take longer than xxx milliseconds to execute are recorded . Slow Logslowlog-max-len xxx #the length of the Slow Log, i.e. how many Slow Log entries are kept at most
- Use the slowlog get [number] command to output the most recent number of commands that entered the Slow Log.
Use the slowlog reset command to reset the Slow Log
Latency caused by the network
- Use long connections or a connection pool as much as possible; avoid frequently creating and destroying connections
- Batch data operations from the client should be completed in a single interaction using the Pipeline feature.
Latency caused by data persistence
Set a sensible persistence strategy according to the data’s safety level and performance requirements
- Although AOF + fsync always can absolutely guarantee data safety, it triggers an fsync on every operation, which has a fairly obvious impact on Redis performance
- AOF + fsync every second is a good compromise, fsyncing once per second
- AOF + fsync never gives the best performance under the AOF persistence approach
Using RDB persistence usually gives higher performance than AOF, but you need to pay attention to the RDB strategy configuration - Every RDB snapshot and AOF rewrite requires the Redis main process to fork. The fork itself can be time-consuming, depending on the CPU and how much memory Redis is using. Configure the timing of RDB snapshots and AOF rewrites sensibly for your situation, to avoid the latency caused by forking too frequently
Latency caused by swap
When Linux moves the memory pages Redis is using into swap space, it blocks the Redis process and causes abnormal latency in Redis. Swap usually happens when physical memory is insufficient or when some processes are doing a lot of I/O, and you should avoid both situations as much as possible.
The /proc//smaps file keeps a record of a process’s swap usage; by looking at this file you can judge whether Redis’s latency is caused by swap. If the file records a large Swap size, then the latency is very likely caused by swap.Latency caused by data eviction
When a large number of keys expire within the same second, that also causes Redis latency. Try to stagger the keys’ expiration times in use.
Master-slave replication and cluster sharding
Master-slave replication
Redis supports a master-slave replication architecture with one master and many slaves. One Master instance handles all write requests, and the Master syncs write operations to all Slaves.
With Redis replication you can achieve read/write splitting and high availability
- Read requests that don’t require especially low latency can be completed on a Slave, improving efficiency. This is especially true of periodic statistics jobs, which may need to run long-running Redis commands — you can dedicate one or a few Slaves to serving those statistics jobs
- Redis Sentinel gives you high availability: when the Master crashes, Redis Sentinel can automatically promote a Slave to Master and keep providing service
Sentinel performs automatic failover
Redis’s replication feature itself only syncs data; it doesn’t provide monitoring or automatic failover capability. To achieve Redis high availability through replication, you need to bring in one more component: Redis Sentinel
Redis Sentinel is the monitoring component developed by the official Redis team. It can monitor the state of Redis instances, automatically discover Slave nodes through the Master node, elect a new Master when it detects that the Master node has failed, and push the new master-slave configuration to all Redis instances- sentinel monitor mymaster 127.0.0.1 6379 2 #the Master instance’s IP and port, plus the number of affirmative votes needed for the election
- sentinel down-after-milliseconds mymaster 60000 #how long without a response before the Master is considered failed
- sentinel failover-timeout mymaster 180000 #the interval between two failover attempts
- sentinel parallel-syncs mymaster 1 #if there are multiple Slaves, this setting specifies how many Slaves sync data from the new Master at the same time, avoiding all Slaves syncing simultaneously and making the query service unavailable too
Cluster sharding
- The amount of data stored in Redis is large, and the physical memory of a single host can no longer hold it
- The concurrency of write requests to Redis is large, and one Redis instance can no longer carry it
Drawbacks
Cache and database dual-write consistency
- Reduces the probability of inconsistency, but can’t avoid it entirely
- Only eventual consistency can be guaranteed
First, adopt a correct update strategy: update the database first, then delete the cache. Second, because deleting the cache can fail, just provide a compensating measure, for example using a message queue.
Cache avalanche
- A cache avalanche is when a large swath of the cache expires at the same moment, and then another wave of requests arrives, so all the requests slam into the database, causing database connection failures
Cache penetration
- Cache penetration is when an attacker deliberately requests data that doesn’t exist in the cache, so all the requests slam into the database and the database connection fails
Cache breakdown
- Put a mutex lock on the first request that queries the data. Other threads reaching this point can’t get the lock, so they wait; once the first thread has queried the data and populated the cache, later threads coming in find the cache already there and go straight to it.
Advantages
Purely in-memory operation
- Redis keeps all data in memory, and outside of data sync during normal operation it never needs to read from disk — zero I/O. Memory response time is roughly 100 nanoseconds
Single-threaded operation, avoiding frequent context switching
- First, a single thread simplifies algorithm implementation — concurrent data structures are both hard to implement and troublesome to test. Second, a single thread avoids the cost of thread switching and of locking and releasing locks; for server-side development, locks and thread switching are usually performance killers. Of course, single-threading has its drawbacks too, and it’s Redis’s nightmare: blocking. If one command takes too long to execute it blocks other commands, which is fatal for Redis, so Redis is a database aimed at fast-execution scenarios.
Uses a non-blocking I/O multiplexing mechanism
- When you use read or write on a file descriptor (FD), if no data has arrived then the thread is suspended until data arrives
I/O multiplexing actually means that the management of multiple connections can happen in the same process. “Multi” refers to the network connections; “multiplexing” is just the same thread
- Redis uses epoll as its I/O multiplexing implementation, and together with Redis’s own event handling model it converts epoll’s read, write, close and so on into events, so it doesn’t waste too much time on network I/O. It monitors reads and writes on multiple FDs, improving performance.
Policies and memory eviction mechanisms
Deletion mechanisms
Periodic deletion
- A timer watches keys and deletes them automatically when they expire. Memory is released promptly, but it consumes a lot of CPU resources. Under highly concurrent requests, the CPU should be spending its time on requests, not on deleting keys
Lazy deletion strategy
- The so-called lazy strategy is that when a client accesses the key, Redis checks the key’s expiration time, and if it has expired it deletes it immediately without returning anything to you.
Memory eviction policies
noeviction
- When there isn’t enough memory to hold newly written data, new write operations return an error
allkeys-lru
- When there isn’t enough memory to hold newly written data, evict the least recently used key from the keyspace
allkeys-random
- When there isn’t enough memory to hold newly written data, evict a random key from the keyspace
volatile-lru
- When there isn’t enough memory to hold newly written data, evict the least recently used key from the keyspace of keys that have an expiration time set. Not recommended
volatile-random
- When there isn’t enough memory to hold newly written data, evict a random key from the keyspace of keys that have an expiration time set. Not recommended
volatile-ttl
- When there isn’t enough memory to hold newly written data, evict keys with an earlier expiration time first from the keyspace of keys that have an expiration time set. Not recommended
Persistence strategies
Snapshot (RDB)
- A snapshot is a binary serialization of the in-memory data, and is very compact in storage
- RDB works by having the Redis main process fork a child process and letting the child perform disk I/O to carry out RDB persistence. RDB records the data
Append-only log (AOF)
- The AOF log is a continuous incremental backup, and over a long run it becomes enormously large; when the database restarts it has to load the AOF log and replay the commands, and that takes an extremely long time
- The AOF log stores the ordered sequence of commands of the Redis server; it only records the commands that modify memory. AOF records the commands
Saving memory
Control the length of keys
Avoid storing bigkeys
- String: keep the size under 10KB
- List/Hash/Set/ZSet: keep the element count under 10,000
Choose appropriate data types
- String, Set: store int data whenever possible
- Hash, ZSet: keep the number of stored elements below the conversion threshold so they’re stored as a ziplist, saving memory
Use Redis as a cache
Set maxmemory + an eviction policy on the instance
- volatile-lru / allkeys-lru: keep recently accessed data in preference
- volatile-lfu / allkeys-lfu: keep the most frequently accessed data in preference (supported in 4.0+)
- volatile-ttl : evict data that is about to expire first
- volatile-random / allkeys-random: evict data at random
Reliability
Deploy instances by business line
- The first step to improving reliability is “resource isolation”.
You’d best deploy Redis instances by business line, so that when one instance fails it doesn’t affect the other businesses.
This resource isolation approach has the lowest implementation cost, but the payoff is very large
- The first step to improving reliability is “resource isolation”.
Deploy a master-slave cluster
- If you only use a standalone Redis, you run the risk of a machine going down and the service becoming unavailable.
So you need to deploy “multiple replica” instances, i.e. a master-slave cluster, so that when the master goes down a slave is still available, avoiding the risk of data loss and reducing service downtime.
When deploying a master-slave cluster, you also need to note that the master and slaves must be distributed across different machines; avoid cross-deployment.
The reason is that normally the Redis master carries all read and write traffic, so you must prioritize the master’s stability — even if a slave machine misbehaves, it must not affect the master.
Moreover, sometimes we need to do routine maintenance on Redis, such as scheduled data backups — you can then do it only on the slaves, which consumes only slave machine resources and avoids affecting the master
- If you only use a standalone Redis, you run the risk of a machine going down and the service becoming unavailable.
Configure the replication parameters sensibly
Unreasonable
- Replication breaks
- A slave starts a full resync, and master performance suffers
Reasonable
- Set a sensible repl-backlog parameter: too small a repl-backlog, in scenarios with heavy write traffic, means a replication interruption risks a full data resync
- Set a sensible slave client-output-buffer-limit: when replication on the slave has problems, too small a buffer overflows the slave’s buffer and breaks replication
Deploy a Sentinel cluster for automatic failover
- If you only deploy master and slave nodes, failures can’t be switched over automatically, so you also need to deploy a Sentinel cluster for “automatic failover”.
Also, multiple Sentinel nodes need to be distributed across different machines, and their number should be odd, to prevent a Sentinel election from failing and delaying the switchover
- If you only deploy master and slave nodes, failures can’t be switched over automatically, so you also need to deploy a Sentinel cluster for “automatic failover”.
Day-to-day operations
Never use the KEYS/FLUSHALL/FLUSHDB commands
Running these commands blocks the Redis main thread for a long time, which is extremely harmful
- Use SCAN instead of KEYS
- On 4.0+ you can use FLUSHALL/FLUSHDB ASYNC, which runs the data-clearing operation in a background thread
Set a sleep interval when scanning production instances
- Whether you’re using SCAN to scan a production instance or doing bigkey statistical analysis on an instance, I suggest you always remember to set a sleep interval while scanning.
This prevents the instance’s OPS from being so high during the scan that it causes Redis performance jitter
- Whether you’re using SCAN to scan a production instance or doing bigkey statistical analysis on an instance, I suggest you always remember to set a sleep interval while scanning.
Use the MONITOR command with caution
- Sometimes when troubleshooting Redis problems you’ll use MONITOR to look at the commands Redis is currently executing.
But if your Redis OPS is relatively high, running MONITOR makes the memory used by Redis’s output buffer keep growing, which heavily consumes Redis memory resources and can even push the instance’s memory past maxmemory, triggering data eviction — you need to be especially careful about this
- Sometimes when troubleshooting Redis problems you’ll use MONITOR to look at the commands Redis is currently executing.
Slaves must be set to slave-read-only
- Your slaves must be set to slave-read-only state, to avoid writing data to a slave and making master and slave data inconsistent.
In addition, if a slave is in a non-read-only state and you’re using Redis below 4.0, it has this bug:
Data with an expiration time written to the slave isn’t periodically cleaned up and its memory isn’t released.
This causes memory leaks on the slave! The problem wasn’t fixed until version 4.0, so be especially careful when configuring slaves
- Your slaves must be set to slave-read-only state, to avoid writing data to a slave and making master and slave data inconsistent.
Configure the timeout and tcp-keepalive parameters sensibly
- If for network reasons a large number of your client connections to Redis are unexpectedly interrupted, and your Redis maxclients setting happens to be relatively small, this can leave clients unable to establish new connections with the server (the server considers maxclients exceeded).
The cause of this problem is that every time a client establishes a connection with the server, Redis assigns that client a client fd.
When a network problem occurs between client and server, the server doesn’t release that client fd immediately.
So when does it release it?
Redis has an internal scheduled task that periodically checks whether every client’s idle time exceeds the configured timeout value.
If Redis hasn’t enabled tcp-keepalive, the server only cleans up and releases that client fd after the configured timeout elapses.Before that cleanup happens, if a large number of new connections come in, the client fds held inside the Redis server can exceed maxclients, and new connections then get refused.
For this situation, my optimization advice is:
Don’t configure too high a timeout: let the server clean up invalid client fds as quickly as possible
Enable tcp-keepalive on Redis: the server then periodically sends TCP heartbeat packets to clients to check connection liveness, so when the network misbehaves zombie client fds get cleaned up as soon as possible- If for network reasons a large number of your client connections to Redis are unexpectedly interrupted, and your Redis maxclients setting happens to be relatively small, this can leave clients unable to establish new connections with the server (the server considers maxclients exceeded).
When adjusting maxmemory, pay attention to the order for master and slaves
- If a slave’s memory exceeds maxmemory, that also triggers data eviction.
In some scenarios a slave can reach maxmemory before the master (for example, running the MONITOR command on the slave, where the output buffer uses a lot of memory), and at that point the slave starts evicting data, making master and slave inconsistent.
To avoid this problem, when adjusting maxmemory you must pay attention to the order in which you modify master and slaves:
Increasing maxmemory: modify the slaves first, then the master
Decreasing maxmemory: modify the master first, then the slaves
It wasn’t until Redis 5.0 that Redis added a replica-ignore-maxmemory setting, where by default a slave exceeding maxmemory won’t evict data, which finally solved this problem
- If a slave’s memory exceeds maxmemory, that also triggers data eviction.
Preventing Redis problems
Sensible resource planning
- Make sure the machine has enough CPU, memory, bandwidth and disk resources
- Do capacity planning ahead of time; reserve half the memory resources on master machines, to prevent a network failure between master and slave machines triggering a large-scale full sync that leaves the master machine short on memory
- Keep a single instance’s memory under 10G; large instances risk blocking during master-slave full sync and RDB backups
Solid monitoring and alerting
- Monitor machine CPU, memory, bandwidth and disk, and alert promptly when resources run short — any shortage of resources affects Redis performance
- Set a sensible slowlog threshold and monitor it; alert promptly when there are too many slowlogs
- When the monitoring component collects Redis INFO, use long connections and avoid frequent short connections
- Do runtime monitoring of the instance, focusing on the expired_keys, evicted_keys and latest_fork_usec metrics; a sudden short-term spike in these metrics can mean blocking risk
For the commands above, try to avoid passing parameters like [0 -1] or [-inf +inf] to do a one-shot full traversal of a Sorted Set, especially when the Sorted Set’s size is unpredictable. You can use the ZSCAN command for cursor-based traversal, or use the LIMIT parameter to limit the number of members returned (applicable to the ZRANGEBYSCORE and ZREVRANGEBYSCORE commands) to achieve cursor-based traversal
The time complexity is O(N), and N grows as the number of keys in Redis increases; so when Redis has a large number of keys, the keys command takes a very long time to execute, and since Redis is single-threaded, one command that takes too long means all the requests behind it can’t get a response

