redis expiration policy

REDIS expiration policy

Introduction

In the scenario of using redis as a cache, the memory usage efficiency of redis is determined by the memory elimination strategy. In most scenarios, we will use LRU (Least Recently Used) as the elimination strategy of redis;

LRU

LRU算法作为内存管理的一种有效算法,其含义是在内存有限的情况下,当内存容量不足时,为了保证程序的运行,这时就不得不淘汰内存中的一些对象,释放这些对象占用的空间;
每次淘汰最近最少使用的元素,一般采用对存储在内存的元素采用 ‘age bits’ 来标记该元素从上次访问到现在为止的时长,从而在每次用LRU淘汰时,淘汰这些最长时间未被访问的元素;
在操作系统中LRU算法淘汰的不是内存中的对象,而是页,当内存中数据不足时,通过LRU算法,选择一页(一般是4KB)将其交换到虚拟内存区(Swap区);
LRU算法最为经典的实现,就是HashMap+Double LinkedList,时间复杂度为O(1);

Common Expiration Strategies

Delete regularly

含义:
    在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除
优点:
    保证内存被尽快释放
缺点:
    若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key
    定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器产生),性能影响严重

lazy delete

含义:
    key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null。
优点:
    删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步
缺点:
    若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)

delete regularly

含义:每隔一段时间执行一次删除过期key操作
优点:
    通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用--处理"定时删除"的缺点
    定期删除过期key--处理"惰性删除"的缺点
缺点:
    在内存友好方面,不如"定时删除"
    在CPU时间友好方面,不如"惰性删除"
难点:
    合理设置删除操作的执行时长(每次删除执行多长时间)和执行频率(每隔多长时间做一次删除)

Expiration strategy adopted by Redis: lazy deletion + regular deletion

Periodic deletion process (simply put, randomly delete expired keys less than or equal to the specified number of each library of the specified number of libraries)

遍历每个数据库(就是redis.conf中配置的"database"数量,默认为16)
检查当前库中的指定个数个key(默认是每个库检查20个key,注意相当于该循环执行20次,循环体时下边的描述)
如果当前库中没有一个key设置了过期时间,直接执行下一个库的遍历
随机获取一个设置了过期时间的key,检查该key是否过期,如果过期,删除key
判断定期删除操作是否已经达到指定时长,若已经达到,直接退出定期删除。

Lazy delete process

在进行get或setnx等操作时,先检查key是否过期,
若过期,删除key,然后执行相应操作;
若没过期,直接执行相应操作

simply put:

1.主动淘汰
    1.1 通过定时任务serverCron定期的清理过期的key(serverCron每间隔1000/hz ms会调用databasesCron方法来检测并淘汰过期的key)
2.被动淘汰
    2.1 每次写入key时,发现内存不够,调用activeExpireCycle释放一部分内存
    2.2 每次访问相关的key,如果发现key过期,直接释放掉该key相关的内存

Expiration policy options for REDIS

The process of reclaiming memory:

1. The client initiates a command that needs to apply for more memory;
2. Redis checks the memory usage. If the used memory is greater than maxmemory, it starts to eliminate the memory (key) according to the different elimination strategies configured by the user, in exchange for a certain amount of memory ;
3. If there is no problem with the above, the command is executed successfully;

Notice:

内存耗尽时,如果你打开虚拟内存功能,Redis就会把那些不经常使用的数据存储到磁盘;
如果Redis里的虚拟内存被禁了,他就会用上操作系统的虚拟内存(交换内存),同时性能急剧下降;
你可以配置maxmemory参数,来避免Redis默认再分配更多的内存;
maxmemory为0的时候表示我们对Redis的内存使用没有限制;

maxmemory-policy: expiration policy option

Regardless of whether Redis has set expire or not, it will follow the configured deletion mechanism of redis. The default strategy is noeviction strategy; the
memory elimination strategy mainly uses 6 ways to release memory objects:

    volatile-lru
        从设置了过期时间的数据集中,选择最近最久未使用的数据释放
    allkeys-lru
        从数据集中(包括设置过期时间以及未设置过期时间的数据集中),选择最近最久未使用的数据释放
    volatile-random
        从设置了过期时间的数据集中,随机选择一个数据进行释放
    allkeys-random
        从数据集中(包括了设置过期时间以及未设置过期时间)随机选择一个数据进行入释放
    volatile-ttl
        从设置了过期时间的数据集中,采取TTL算法(最小存活时间)选择数据进行释放操作
    noeviction
        不删除任意数据(但redis还会根据引用计数器进行释放),这时如果内存不够时,会直接返回错误

The default memory strategy is noeviction. In Redis, the LRU algorithm is an approximate algorithm. By default, Redis randomly selects 5 keys, and selects one of the most recently unused keys for elimination;
in the configuration file, you can pass maxmemory-samples The value of Redis needs to check the number of keys to set the number of keys, but the more keys are checked, the longer it takes, but the more accurate the structure;

LRU execution mechanism of REDIS

Inside redis, the relevant information after redis is started is saved through the global structure struct redisServer, such as:

    struct redisServer {
       pid_t pid; /* Main process pid. */
       char *configfile; /* Absolute config file path, or NULL */
       …..
       unsigned lruclock:LRU_BITS; /* Clock for LRU eviction */
       ...
    };

redisServer contains the basic information after the redis server is started (PID, configuration file path, serverCron running frequency hz, etc.), external callable module information, network information, RDB/AOF information, log information, replication information, etc.

In the above structure, lruclock: LRU_BITS, which stores the lru clock after the server is started, which is the global lru clock;
the clock is 100ms (can be adjusted by hz, the default is hz=10, so every 1000ms/10=100ms Execute a scheduled task) to update once;

server.lruclock performs the update operation in the timer running in redis.c, the code is as follows (the timer in redis.c is configured to execute once every 100ms):

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
    .....
    run_with_period(100) trackOperationsPerSecond();

    /* We have just REDIS_LRU_BITS bits per object for LRU information.
     * So we use an (eventually wrapping) LRU clock.
     *
     * Note that even if the counter wraps it's not a big problem,
     * everything will still work but some object will appear younger
     * to Redis. However for this to happen a given object should never be
     * touched for all the time needed to the counter to wrap, which is
     * not likely.
     *
     * Note that you can change the resolution altering the
     * REDIS_LRU_CLOCK_RESOLUTION define. */
    server.lruclock = getLRUClock();
    ....
    return 1000/server.hz;
}

The redisObj declared in redis.h defines the storage object of the value corresponding to the redis key:

#define REDIS_LRU_BITS 24
#define REDIS_LRU_CLOCK_MAX ((1<<REDIS_LRU_BITS)-1) /* Max value of obj->lru */
#define REDIS_LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */
typedef struct redisObject {<br>  //存放的对象类型
    unsigned type:4;
    //内容编码
    unsigned encoding:4;
    //与server.lruclock的时间差值
    unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */\
    //引用计数算法使用的引用计数器
    int refcount;
    //数据指针
    void *ptr;
} robj;

The most critical sentence in the code is o->lru=LRU_CLOCK(), which is a definition, look at the implementation of this macro definition, the code is as follows

#define LRU_CLOCK() ((1000/server.hz <= REDIS_LRU_CLOCK_RESOLUTION) ? server.lruclock : getLRUClock())

Among them, REDIS_LRU_CLOCK_RESOLUTION is 1000, which can be configured in the configuration file by yourself, which indicates the accuracy of the LRU algorithm. Here we can see the usefulness of server.lruclock. If the frequency of the timer execution is higher than the accuracy of the LRU algorithm, You can directly assign server.lruclock when the object is created, avoiding the memory overhead and time overhead of function calls

With the above foundation, the following is the most critical part, the LRU algorithm in REDIS, here takes volatile-lru as an example (select the data set with expiration time for elimination), when the command is processed in Redis, the processCommand will be called Function, in the ProcessCommand function, when maxmemory is configured in the configuration file, the freeMemoryIfNeeded function will be called to release unused memory space

Summarize

1. As a cache, redis often uses the LRU strategy to eliminate data, so if there are too many expired data at the same time, it will take too long for redis to initiate active detection (maximum 250ms), resulting in the maximum application timeout> = 250ms;
2. If the memory usage rate is too high, it will lead to insufficient memory, thus initiating a passive elimination strategy, which will make the application access timeout;
3. Reasonably adjust the hz parameter to control the frequency of each active elimination, thereby effectively mitigating The above timeout problem caused by too many expired keys;

Reference:
https://www.cnblogs.com/WJ5888/p/4371647.html
https://blog.csdn.net/caishenfans/article/details/44902651
http://blog.jobbole.com/105335/
http: https://blog.jobbole.com/107084/

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324643856&siteId=291194637