A Summary of Everything About Redis

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
  • Geospatial (geo)

    • Geospatial index radius queries
  • Bloom filter

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.

Client tools

  • Connect to a Redis server over a socket

     redis-cli -s /tmp/redis.sock
    
  • Connect to a Redis server without a socket

    redis-cli
    

Performance testing tools

  • Test with the default parameters

    redis-benchmark
    
  • Test with custom parameters

    redis-benchmark -n 1000000 --csv
    
  • Xueqiu rdr:

          https://github.com/xueqiu/rdr
    
  • redis-rdb-tools:

    https://github.com/sripathikrishnan/redis-rdb-tools
    

Utility commands

  • Start the service with a specified config file

    redis-server redis.conf
    
  • Start 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/enabled
    
  • Deploy 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
  • 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
  • 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
  • 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
  • Compress data before writing it to Redis

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
  • 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
  • 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

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
  • 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
  • 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
  • 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

  • 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

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