redis将数据存储到内存,占用内存大小有一个设置阈值,当内存凌驾阈值时,必要根据设置的内存淘汰策略进行键的淘汰来低落内存占用,使redis可以或许继续进行写操作。这里我们参考redis7.0源码,来看一下当超出内存限定时,redis是怎么进行键淘汰的。
1. 源码入口
在源码中,这个过程在performEvictions函数中, 该函数在处理每条命令时被调用,调用链如下图:
假如redis设置了maxmemory,同时redis没有处在超时执行的lua脚本,则进行内存淘汰检查和执行对应的淘汰策略。
2. 相关设置
启动设置文件中下面这些设置项与内存淘汰息息相关:
- maxmemory <bytes>, redis最大内存限定,假如没有设置,默认值32位体系下为3G,64位体系无限定
- maxmemory-policy,8种内存淘汰策略中的一种
- maxmemory-samples, lru,lfu,ttl采用的是近似算法,维护了一个大小为16的数组,每次采样maxmemory-samples个键更新这个数组
- lazyfree-lazy-eviction,是否启动惰性淘汰,开启的情况下会尝试异步删除
3. 淘汰策略
淘汰策略就是指设置项maxmemory-policy。一共有8种, 从预选键的范围上来看分为3类, noeviction 体现不进行淘汰,allkeys体现在全部键上进行具体的策略,volatile体现只在设置了过期时间的键上进行具体的策略。而具体的淘汰算法实际是4种。
3.1 随机淘汰
遍历每个db,从中随机选择一个key键,留意假如前次选定了db_a,则下次会从db_a的下一个db开始选择。而随机选择的策略,对应到源码中的dictGetRandomKey(dict)。dictGetRandomKey包罗两个步骤,首先是随机选择一个hash表中的slot, 其次在slot的拉链上选择一个键。
选择slot的时候会思量rehash,不会选择刚刚rehash竣事的键。
3.2 LRU(Least Recently Used)
LRU/LFU/TTL策略在同一个条件分支下。首先是遍历每个db,调用evictionPoolPopulate函数,采样若干个键放入pool中, 由于pool中的键是按照idle从小到大排序的,然后从后往前遍历pool获取一个键,即为候选键bestKey。差异的策略实际是idle值计算方式差异。
LRU,优先淘汰最近一段时间没有被访问到的键。调用estimateObjectIdleTime获取键的idle值。
- typedef struct redisObject {
- unsigned type:4;
- unsigned encoding:4;
- unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
- * LFU data (least significant 8 bits frequency
- * and most significant 16 bits access time). */
- int refcount;
- void *ptr;
- } robj;
复制代码 每个键对象包罗了一个lru字段,该字段在LRU淘汰策略下体现上一次被访问时的lru时钟时间, 差值即为该键的空闲时间。同时思量到lru为24位,只能保存一段时间内的值,大概存在值的套圈,所以有了else的条件判断。
3.3 LFU(Least Frequently Used)
LFU思量到访问频率,idle = 255-LFUDecrAndReturn(o);。在LFU策略下,键对象的lru字段低8位体现访问频次,高16位体现前次访问的时间。255-访问频次作为idle的值,访问频率越低idle值越大。
3.4 TTL
直接获取键的过期时间,取最近的过期键。
4. 候选键删除
得到候选键和对应的db后,redis是怎么执行删除的呢?
- 获取当前内存值 delta
- 执行删除,根据设置项决定是同步删除照旧异步删除
- 重新获取内存值, 计算本次删除的内存大小delta(此时异步删大概还没有执行)
- 删除大概连续好久,每开释16次键,同步删除信号给副本;异步删除情况下,检查内存是否已经满足要求
- 检查当前删除耗时是否大于设置值,假如超出时间束缚,启动后台任务继续执行删除
4.1 同步和异步删除
async=0的时候,体现同步删除,aync=1体现异步删除。
删除键所在db假如有设置过期的键聚集,则首先尝试删除该键的过期时间,然后调用dictUnLink将键从字典中移除,但是此时并没有开释键和节点的内存。假如是异步删除则调用freeObjAsync异步删除,同时设置该节点对应的值为nullptr。最后调用dictFreeUnlinkedEntry,对于同步的情况,val不为空,会直接删除,而异步的情况,val为空,不执行删除val操作,而是在异步任务中真正的删除。
而异步删除freeObjAsync实际上还会检查对象值是否大于设置阈值以及当前是否没有被共享,两个条件都满足时才会启动后台删除线程。
这个值的计算遵照:string范例为 1,list范例返回存储元素数,set(hashtable实现)、zset(跳表实现)、hash(hashtable)返回包罗元素数
5. 总结
redis在处理线上命令时会检测内存是否超出阈值,当超出阈值时,计算本次必要开释多少内存。然后根据淘汰策略选择删除候选键,随机选择是随机选择字典的槽和拉链上的节点,lru/lfu/ttl 都是采用的近似算法,维护一个固定大小的数组,按照键的idle值从小到大排序,每次选择idle最大的键,每次也会采样指定的键来更新这个数组。选定候选键后根据设置执行同步或者异步删除。为了防止阻塞线上哀求,删除不能执行太久,所以每16次都会检查内存是否已经满足要求,满足则直接退出,检查当前删除动作是否已经超时,超市转化到后台执行。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |