Redis memory consumption and recycling

Redis is an open source, high-performance Key-Value database , which is widely used in various server scenarios. Redis is an in-memory database that stores data in memory, and its read and write efficiency is much faster than traditional databases that store data on disk. Therefore, monitoring the memory consumption of Redis and understanding the Redis memory model are crucial to efficient and long-term stable use of Redis.

Before the introduction, let me explain that in the general production environment, the development colleagues will not be open to the permission to directly connect to the redis cluster. Generally, the daas platform is provided. Through the visual command window, enter the redis command, and generally only have the read permission; for the write operation, you need Mention redis data change orders, and for redis memory, large keys, and slow commands, the information will generally be integrated and displayed on the monitoring board, without the need for development colleagues to enter commands themselves; but basic relevant knowledge is still required.

reids memory analysis

redis memory usage: info memory

Example: It can be seen that the memory fragmentation rate of the current node is 226893824/209522728 ≈ 1.08, and the memory allocator used is jemalloc.

used_memory_rss is usually larger than used_memory because of memory fragmentation.

But when the operating system swaps the redis memory to the hard disk, memory_fragmentation_ratio will be less than 1. Redis uses hard disk as memory, because of the speed of hard disk, redis performance will be greatly affected.

redis memory usage

The memory usage distribution of redis: its own memory, key-value object occupation, buffer memory occupation and memory fragmentation occupation.

The redis empty process itself consumes very little, which can be ignored, and the optimization of memory does not need to consider the factors here.

object memory

Object memory, that is, the memory occupied by the actually stored data.

Redis kv structure storage, object occupancy can be simply understood as k-size + v-size.

The keys of redis are all string types, and the values ​​​​contain multiple types: five basic types: string, list, hash, set, and zset, as well as string-based Bitmaps and HyperLogLog types.

In practical applications, the construction form of kv and memory usage expectations must be well prepared.

buffer memory

Buffer memory consists of three parts: client cache, replication backlog cache, and AOF buffer.

client cache

The memory usage of the input and output buffers of the TCP connection connected to the redis server is uncontrolled, and the maximum allowable space is 1G. The output buffer occupancy can be configured through the client-output-buffer-limit parameter.

Redis clients are mainly divided into slave clients, subscription clients and common clients.

  • connection from client

That is what we call slave. The master node will establish a connection for each slave node for command replication. The buffer configuration is: client-output-buffer-limit slave 256mb 64mb 60.

The network delay between master and slave and the number of mounted slave nodes are the main factors affecting memory usage. Therefore, special attention should be paid when it comes to the need to deploy masters and slaves in different places. In addition, it is also necessary to avoid mounting too many slave nodes on the master node (<=2);

  • Subscription client memory usage

The publish-subscribe function connects the client to use a separate buffer, the default configuration: client-output-buffer-limit pubsub 32mb 8mb 60.

When consumption is slower than production, it will cause a backlog in the buffer zone, so special attention needs to be paid to the ratio of consumer roles and the monitoring of production and consumption speeds.

  • Ordinary client memory usage

For clients other than the above, such as our usual application connection, the default configuration is: client-output-buffer-limit normal 1000.

It can be seen that ordinary clients do not have a buffer limit configured, and usually general client memory consumption is also negligible.

However, when the redis server responds slowly, it is easy to cause a large number of slow connections, mainly manifested as a sudden increase in the number of connections. If it cannot be processed in time, it will seriously affect the service and recovery of the redis service node.

Regarding this, there are a few points to note in practical applications:

  • maxclients The configuration of the maximum number of connections is essential.

  • Reasonably estimate the data volume (write or read) of a single operation and the network delay ttl.

  • Online high-throughput command operations, such as keys, are prohibited.

In high-concurrency application scenarios, redis memory usage requires a real-time monitoring and early warning mechanism.

copy backlog buffer

A reusable fixed-size buffer provided after v2.8 is used to realize the partial copy function to the slave node and avoid full copy. Configuration singular: repl-backlog-size, default 1M. A single master configures a replication backlog buffer.

AOF buffer

Incremental write commands are saved during AOF rewriting, and the size of this part of the cache depends on the AOF rewriting time and increment.

memory fragmentation

Fixed-range block allocation of memory. Redis uses jemalloc memory allocator by default, others include glibc and tcmalloc.

The memory allocator will first allocate the manageable memory into memory blocks of different sizes to prepare for different data storage requirements. However, we know that the data that needs to be stored in actual applications varies in size and specifications. The memory allocator can only Select the memory block closest to the size of the data requirement for allocation, so that there will be waste of fragments that "occupy not enough" space.

Jemalloc has a corresponding optimization strategy for memory fragmentation, and the normal fragmentation rate of mem_fragmentation_ratio is around 1.03.

In the second part, we said that frequent append and range operations on strings will cause memory fragmentation problems. In addition, in the seventh part, SDS lazy memory recovery will also lead to memory fragmentation. At the same time, the memory recovery of expired keys is also accompanied by the release of space. It cannot be fully utilized, resulting in an increase in the memory fragmentation rate.

Fragmentation:

  • Application level: try to avoid differentiated key-value usage, and do a good job of data alignment.

  • Redis service level: defragmentation can be performed by restarting the service.

maxmemory 及 maxmemory-policy

Redis controls the maximum available memory and memory recovery of redis based on the above configuration. It should be noted that the execution of memory recycling affects the performance of redis, so as to avoid frequent memory recycling overhead.

redis child process memory consumption

The child process is the subtask process of fork when redis performs persistence (RDB/AOF).

About the copy-on-write mechanism of the linux system

The parent and child processes will share the same physical memory page. When the parent process processes the write request, it will copy a copy of the page that needs to be modified and modify it. The memory read by the child process is the memory snapshot of the parent process at the time of fork. Therefore, the memory of the child process Memory consumption is determined by the increment of write operations in between.

About Linux's transparent huge page mechanism THP (Transparent Huge Page)

The THP mechanism will reduce the speed of the fork child process: the copy-on-write memory page is increased from 4KB to 2M. In high concurrency scenarios, copy-on-write memory usage and consumption will have a great impact, so it needs to be selectively disabled.

About linux configuration

Generally, it is necessary to configure the linux system vm.overcommit_memory = 1 to allow the system to allocate all physical memory. Prevent fork tasks from failing due to memory.

redis memory management

The memory management of redis is mainly divided into two aspects: memory upper limit control and memory recycling management.

Memory upper limit: maxmemory

Purpose: triggering of cache application memory recovery mechanism + prevent physical memory exhaustion (redis uses unlimited server memory by default) + service node memory isolation (deploy multiple redis service nodes on a single server)

When performing memory allocation and limitation, the impact of memory fragmentation should be fully considered. Dynamically adjust and expand the available memory of redis service nodes:config set maxmemory {}

memory recovery

Recycling timing: key expires, memory usage reaches upper limit

Expired key deletion

The redis key expiration time is stored in the internal expiration dictionary, and redis uses a lazy deletion mechanism + a scheduled task deletion mechanism.

  • lazy delete

That is, delete when reading, when reading a key with a timeout attribute, if the key has expired, delete and return a null value. The problem with this method is that when the trigger timing is added and the expired key has not been read for a long time, it will always be stored in the memory, causing a memory leak.

  • Timed task deletion

Redis internally maintains a scheduled task (10 times per second by default, configurable), which is deleted through an adaptive method.

The deletion logic is as follows:

One thing that needs to be explained is that the deletion logic executed by the fast and slow modes is the same, but the timeout period is different.

Memory overflow control

When the memory reaches maxmemory, the memory recovery policy will be triggered, and the specific policy is executed according to maxmemory-policy.

  • noevication: By default, no recovery is performed. If the memory limit is reached, write operations will no longer be accepted and an error will be returned.

  • volatile-lru: Delete keys with an expiration time set according to the LRU algorithm. If there is no key, no recycling will be performed.

  • allkeys-lru: Delete keys according to the LRU algorithm, for all keys.

  • allkeys-random: Randomly delete keys.

  • volatile-random: Randomly delete keys with an expiration time set.

  • volatile-ttl: According to the key ttl, delete the most recently expired key, and if no expired key is set, delete will not be performed.

Dynamic configuration:config set maxmemory-policy {}

When maxmemory is set, each redis operation will check and perform memory recovery, so for the online environment, make sure that all maxmemory > used_memory.

In addition, you can actively trigger memory reclamation by dynamically configuring maxmemory .

Memory recovery strategy

There are two types of memory recovery triggers, that is, the overflow recovery triggered when the memory usage reaches the upper limit of maxmemory, and the other is the memory recovery triggered when the expired object expires.

Overflow recovery triggered when Redis memory usage reaches the upper limit of maxmemory; Redis provides several strategies (maxmemory-policy) to allow users to decide how to free up new space to continue providing read and write services:

  • server.db[i].expires(1) volatile-lru: Select the least recently used data from the data set ( ) with an expiration time set to eliminate

  • (2) volatile-ttl: server.db[i].expiresSelect the data that will expire from the data set ( ) with an expiration time set to be eliminated

  • server.db[i].expires(3) volatile-random: Randomly select data elimination from the data set ( ) with an expiration time set

  • (4) allkeys-lru: When the memory is not enough to accommodate the newly written data, in the key space, remove the least recently used key (this is the most commonly used)

  • (5) allkeys-random: server.db[i].dictRandomly select data from the data set ( ) to eliminate

  • (6) no-eviction: data eviction is prohibited, that is to say, when the memory is not enough to accommodate the newly written data, the new write operation will report an error. This should not be used by anyone!

  • The following two types are added after version 4.0:

  • server.db[i].expires(7) volatile-lfu: Select the least frequently used data from the data set ( ) with an expiration time set to eliminate

  • (8) allkeys-lfu: When the memory is not enough to accommodate the newly written data, in the key space, remove the least frequently used key

The default strategy of redis is the noeviction strategy. If you want to configure it, you need to write this configuration in the configuration file:

maxmemory-policy volatile-lru

Redis's LRU algorithm

LRU is the Least Recently Used algorithm. Many cache strategies use this strategy to release space. This mechanism is also used to reclaim memory when learning the memory recovery of the operating system. There is also LFU ( Least Frequently Used) least frequently used algorithm, this algorithm.

We can also learn from the above description that redis uses an LRU-like algorithm for memory overflow recovery, and its algorithm code:

/* volatile-lru and allkeys-lru policy */
else if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU ||
 server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)
{
 struct evictionPoolEntry *pool = db->eviction_pool;
 
 while(bestkey == NULL) {
  evictionPoolPopulate(dict, db->dict, db->eviction_pool);
  /* Go backward from best to worst element to evict. */
  for (k = REDIS_EVICTION_POOL_SIZE-1; k >= 0; k--) {
   if (pool[k].key == NULL) continue;
   de = dictFind(dict,pool[k].key);
 
   /* Remove the entry from the pool. */
   sdsfree(pool[k].key);
   /* Shift all elements on its right to left. */
   memmove(pool+k,pool+k+1,
    sizeof(pool[0])*(REDIS_EVICTION_POOL_SIZE-k-1));
   /* Clear the element on the right which is empty
    * since we shifted one position to the left.  */
   pool[REDIS_EVICTION_POOL_SIZE-1].key = NULL;
   pool[REDIS_EVICTION_POOL_SIZE-1].idle = 0;
 
   /* If the key exists, is our pick. Otherwise it is
    * a ghost and we need to try the next element. */
   if (de) {
    bestkey = dictGetKey(de);
    break;
   } else {
    /* Ghost... */
    continue;
   }
  }
 }
}

Redis will select a fixed number of keys based on the server.maxmemory_samples configuration, then compare their LRU access times, and then eliminate the keys that have not been accessed for the longest time. The larger the value of maxmemory_samples, the closer Redis's approximate LRU algorithm is to the strict LRU algorithm. But the corresponding consumption is also higher. Therefore, frequent memory recovery of this kind will reduce the performance of redis, mainly because of the overhead of finding recovery nodes and deleting nodes that need to be recovered.

So generally when we configure redis, try not to let it perform this kind of memory overflow recovery operation. Redis can configure maxmemory, and used_memory refers to the memory actually occupied by redis, but because the operating system has other software and memory fragments are still There is a swap area, so our actual memory should be larger than the maxmemory set in redis. The specific size depends on the system environment and software environment. maxmemory is also larger than used_memory. Generally, due to the existence of fragments, it needs to be rich by 1~2 G.

Link: https://blog.csdn.net/minghao0508/article/details/124143905

Guess you like

Origin blog.csdn.net/LinkSLA/article/details/130677994