What happens to Redis when memory runs out

Preface

As a server, the memory is not unlimited, there's always the case of memory exhaustion, then when Redisthe server out of memory, if it continues to perform the requested command, Rediswill be how to handle it?

Memory reclamation

When using the Redisservice, in many cases, some key-value pairs will only be valid for a certain period of time. In order to prevent this type of data from occupying memory all the time, we can set the validity period for the key-value pairs. RedisIt may be provided through 4a separate command to set the expiration time to a key:

  • expire key ttl: The keyexpiration time value is set ttl in seconds .
  • pexpire key ttl: The keyexpiration time value is set ttl in milliseconds .
  • expireat key timestamp: The keyexpiration time value to the specified timestamp number of seconds .
  • pexpireat key timestamp: The keyexpiration time value to the specified timestamp number of milliseconds .

PS: No matter which command to use, and ultimately Redisthe bottom are using the pexpireatcommand to achieve. Further, setcommands may be provided keyat the same time plus the expiration time, it can guarantee atomicity set value, and a time of expiration.

After setting the expiration date, can ttland pttl(if you do not set the expiration time of the command returns the following two commands to query the two remaining expiration time -1, if you set an invalid expiration time, then return -2):

  • ttl keyReturns the keyremaining number of seconds expired.
  • pttl keyReturns the keynumber of milliseconds remaining expired.

Expiration strategy

If you delete an expired key, we generally have three strategies:

  • Timed deletion: Set a timer for each key, once the expiration time is up, delete the key. This strategy is very friendly to memory, but CPUunfriendly, because each timer will take up some CPUresources.
  • Lazy deletion: No matter whether the key has expired or not, it will not be deleted actively, and it will be judged whether it is expired every time the key is obtained. If it expires, the key will be deleted, otherwise the value corresponding to the key will be returned. This strategy is not memory friendly and may waste a lot of memory.
  • Regular scan: The system scans regularly at regular intervals, and deletes expired keys. This strategy is relatively a compromise between the above two strategies. It should be noted that this regular frequency should be controlled in accordance with the actual situation. The use of this strategy has a drawback that keys that have expired may also be returned.

In Redisthem, their choice is a policy 2and strategy 3integrated use. But Redisperiodically scanning only Scan button set expiration time, because the key to set the expiration time Rediswill be stored separately, the situation scans all keys so it will not appear:

typedef struct redisDb {
    
    
    dict *dict; //所有的键值对
    dict *expires; //设置了过期时间的键值对
   dict *blocking_keys; //被阻塞的key,如客户端执行BLPOP等阻塞指令时
   dict *watched_keys; //WATCHED keys
   int id; //Database ID
   //... 省略了其他属性
} redisDb;

8 elimination strategies

If Redisamong all the keys have not expired, and this time the memory is full, then the client continues to execute setwhen such orders Rediswill be how to deal with it? RedisIt provides different elimination strategies to deal with this scenario.

First Redisprovides a parameter maxmemoryto configure the Redismaximum memory usage:

maxmemory <bytes>

Command or may config set maxmemory 1GBbe dynamically modified.

If this parameter is not set, then the 32operating system bit in Redisuse up 3GBmemory in 64the operating system is not limited in the position.

RedisProvides 8seed out of policy, parameters can be maxmemory-policyconfigured:

Elimination strategy Description
volatile-lru The keys with expiration time are deleted according to the LRU algorithm until free space is available. If there is no key object that can be deleted, and the memory is still not enough, an error will be reported
allkeys-lru Delete all keys according to the LRU algorithm until free space is available. If there is no key object that can be deleted, and the memory is still not enough, an error will be reported
volatile-lfu According to the LFU algorithm, delete the key with expiration time until free space is available. If there is no key object that can be deleted, and the memory is still not enough, an error will be reported
allkeys-lfu Delete all keys according to the LFU algorithm until free space is available. If there is no key object that can be deleted, and the memory is still not enough, an error will be reported
volatile-random Randomly delete keys with an expiration time until free space is available. If there is no key object that can be deleted, and the memory is still not enough, an error will be reported
allkeys-random Randomly delete all keys until free space is available. If there is no key object that can be deleted, and the memory is still not enough, an error will be reported
volatile-ttl According to the ttl attribute of the key-value object, delete the data that will expire recently. If not, report an error directly
noeviction The default strategy, do not do any processing, directly report an error

PS: elimination strategy also can be used as a command config set maxmemory-policy <策略>to be dynamically configured.

LRU algorithm

LRUFull Least Recently Usedname: . That is: the longest time it has not been used recently. This is mainly for use time.

Redis's improved LRU algorithm

In Redisthem, we do not use the traditional LRUmethod, because of the traditional LRUexistence algorithm 2problem:

  • Need extra space for storage.
  • There may be some keyvalue to use very often, but recently not used, thereby LRUalgorithms deleted.

In order to avoid the above 2problem, Redisamong the traditional LRUalgorithm was modified to delete by sampling method .

Provides a configuration file attribute maxmemory_samples 5, the default value is 5expressed randomly selected 5a keyvalue, then these 5two keyvalues in accordance with the LRUdeletion algorithm, so obviously, keythe larger the value, the higher the deletion of accuracy.

Sampling of the LRUalgorithm and the conventional LRUalgorithm, Redisthe official website of which there is a comparison chart:

  • The light gray band is the deleted object.

  • The gray bands are objects that have not been deleted.

  • Green is the added object.

    Insert picture description here

The first figure represents the upper-left corner of the traditional LRUalgorithm, we can see that when the number of samples reaches 10a (upper right corner), and has been the traditional LRUalgorithm is very close.

How Redis manages heat data

When we describe a String object in front, it referred to the redisObjectobject placed in a lruproperty:

typedef struct redisObject {
    
    
    unsigned type:4;//对象类型(4位=0.5字节)
    unsigned encoding:4;//编码(4位=0.5字节)
    unsigned lru:LRU_BITS;//记录对象最后一次被应用程序访问的时间(24位=3字节)
    int refcount;//引用计数。等于0时表示可以被垃圾回收(32位=4字节)
    void *ptr;//指向底层实际的数据存储结构,如:SDS等(8字节)
} robj;

lruThe properties are written when the object is created, and updated when the object is accessed. Normal people's thinking is that the final decision whether to delete a key must be subtracted with the current timestamp lru, and the one with the largest difference will be deleted first. But Redisthere is not to do so, Redisin maintaining a global property lru_clock, this property is through a global function serverCronevery 100milliseconds time to update the record of the current unixtime stamp.

Finally decided to delete the data by lru_clocksubtracting the object lruattributes derived. So why Redisbother? Wouldn't it be more accurate to take the global time directly?

This is because doing so avoids updated each object lruattributes when you can take directly to global attributes, without the need to call system function to obtain the system time, so as to enhance the efficiency ( Redisof which there are many details to consider to improve this performance, it can be said It is to optimize the performance as much as possible to the extreme).

But there's a problem, we see that the redisObjectobject lruproperty only 24bits, 24bits can only store 194a time stamp size-day, more than once 194will be restarted from the day after the 0start of computation, so this time it may appear redisObjectobject lruattributes greater than the global lru_clockattributes of the case.

Because of this, the calculation of the time also need to be divided into 2cases:

  • When global lruclock> lru, use lruclock-to lruget free time.
  • When the global lruclock< lruis used lruclock_max(i.e., 194days) - lru+ lruclockobtain idle time.

It should be noted that this calculation method does not guarantee that the longest idle time can be deleted from the sampled data. This is because the first over 194the case few days is not being used, again only the lruclockfirst 2round continues over lruwhen the property is calculated will go wrong.

Such as object Arecord lruis 1days, while lruclockthe second round are to 10-day, and this time it will lead to results only 10-1=9days, in fact, it should be the 194+10-1=203day. But this kind of situation can be said to happen rarely, so this processing method may be deleted inaccurately, but this algorithm itself is an approximate algorithm, so it will not have much impact.

LFU algorithm

LFUFull Least Frequently Usedname: . That is: the least frequently used recently, this is mainly for the frequency of use. This property is also recorded in redisObjectthe lruinternal attributes.

When we use LFUthe recovery strategy, lruattributes the high 16bits used to record the access time (last decrement time: ldt, in minutes), low 8bit is used to record the frequency of access (logistic counter: logc), referred to counter.

Increasing frequency of visits

LFUEach key only counter 8position, it represents the maximum value 255, it Redisuses a probability based on the number of devices to achieve counterannually. r

Given an old access frequency, when a key is accessed, it counteris incremented as follows:

  1. Extraction 0and 1random number between R.
  2. counter-Initial value (default is 5), get a basic difference value, if this difference value is less than 0, then take it directly 0, for the convenience of calculation, record this difference value as baseval.
  3. Probability Pis calculated as 1/(baseval * lfu_log_factor + 1)follows: .
  4. If R < P, the frequency increment counter++( ).

The formula lfu_log_factoris called a logarithmic factor, by default 10, you may be controlled by the parameters:

lfu_log_factor 10

The figure below is a logarithmic factor lfu_log_factorand frequency countergraph growth:

Insert picture description here

You can see that when the logarithmic factor lfu_log_factoris 100time, probably the 10M(1000万)visit will visit will countergrow to 255, and the default 10can support up to 1M(100万)visits counterin order to reach 255the upper limit, which in most of the scenes are enough to meet demand.

Decreasing frequency of visits

If the access frequency counteronly been incremental, then sooner or later to all 255, that counterhas been incremented by a not fully reflect the keyheat, so when one keyafter a period of time is not being accessed, counteralso needs a corresponding decrease.

counterThe rate of reduction by the parameter lfu-decay-timecontrol, default 1, in minutes. The default value is 1expressed: Nthere is no access within minutes and counterwill reduce N.

lfu-decay-time 1

The specific algorithm is as follows:

  1. Get current timestamp, converted to minutes after taking the low 16position (for convenience of subsequent calculations, the value is referred to now).
  2. Remove the object lruhigh attribute 16bit (for convenience of subsequent calculations, the value is referred to ldt).
  3. When lru> now, the default is one cycle ( 16bit, maximum 65535), then take the difference 65535-ldt+now: when lru<= now, take the difference now-ldt(for the convenience of subsequent calculations, this difference is recorded as idle_time).
  4. Remove the profile lfu_decay_timevalue is then calculated: idle_time / lfu_decay_time(For convenience of subsequent calculations, the value is referred to num_periods).
  5. Finally, the counterreduction:counter - num_periods .

Look so complex, in fact, one sentence formula: Remove the current timestamp and the object lruattribute comparison, calculate how long the current is not accessed, such that the calculated results 100minutes is not accessed, removed and then the configuration parameters lfu_decay_timeIf this configuration defaults 1that is, 100/1=100on behalf of 100minutes did not visit, so counterreduced 100.

to sum up

This paper describes the Redisprocess of key policy expired, and when the server memory is not enough Redisof 8species elimination strategy, finally introduced Redistwo main elimination algorithm LRUand LFU.

Guess you like

Origin blog.csdn.net/zwx900102/article/details/113806440