网站后台图片做链接,苏州高新区住建局官网,WordPress模板使用方法,超炫的网站1.Redis内存管理
我们的redis是一个内存型数据库#xff0c;我们的数据也都是放在内存中的#xff0c;内存是有限的空间#xff0c;当数据满了之后#xff0c;我们要怎么样继续保证redis的可用性呢?我们就需要采取点管理措施和机制来保证我们redis的可用性。
在redis.co…1.Redis内存管理
我们的redis是一个内存型数据库我们的数据也都是放在内存中的内存是有限的空间当数据满了之后我们要怎么样继续保证redis的可用性呢?我们就需要采取点管理措施和机制来保证我们redis的可用性。
在redis.conf中通过maxmemory来配置 maxmemory 100mb // 如果配置是0 那么默认是电脑的内存 如果是32bit 隐式大小为3G 在Redis中有两个核心的机制来保证我们的可用性: Redis过期策略、Redis淘汰机制
2. Redis过期策略
那么什么是过期策略。首先我们知道Redis有一个特性就是Redis中的数据我都是可以设置过期时间的如果时间到了这个数据就会从我们的Redis中删除。
那么过期策略就是讲的是我怎么把Redis中过期的数据从我们Redis服务中移除的。
我们可以类比一个例子假如我们把Redis容器比作一个冰箱。冰箱里面也会放菜菜就是我们的数据数据跟菜都会过期。那么我们冰箱里面假如有菜过期了我们一般是怎么发现的呢?
2.1 惰性过期
我们在准备拿冰箱里的食物吃的时候我们就会先去看下这个东西有没有过期如果过期了就仍掉。
那么在Redis里面就是每次在访问操作key的时候判断这个key是不是过期了如果过期了就删除。
源码验证 expireIfNeeded方法(db.c文件)
int expireIfNeeded(redisDb *db, robj *key) {if (!keyIsExpired(db,key)) return 0;/* If we are running in the context of a slave,instead of* evicting the expired key from the database, wereturn ASAP:* the slave key expiration is controlled by themaster that will* send us synthesized DEL operations for expiredkeys.** Still we try to return the right information to thecaller,* that is, 0 if we think the key should be stillvalid, 1 if* we think the key is expired at this time. *///如果配置有masterhost说明是从节点那么不操作删除if (server.masterhost ! NULL) return 1;/* Delete the key */server.stat_expiredkeys;propagateExpire(db,key,server.lazyfree_lazy_expire);notifyKeyspaceEvent(NOTIFY_EXPIRED,expired,key,db-id);//是否是异步删除 防止单个Key的数据量很大 阻塞主线程 是4.0之后添加的新功能默认关闭int retval server.lazyfree_lazy_expire ?dbAsyncDelete(db,key) :dbSyncDelete(db,key);if (retval) signalModifiedKey(NULL,db,key);return retval;
}每次调用到相关指令时才会执行expireIfNeeded判断是否过期。平时不会判断是否过期。
优点: 该策略就可以最大化地节省CPU资源因为它平时都懒得都判断所以也没有啥cpu损耗只有访问的时候我才会去判断一下
缺点: 对内存非常不友好。因为如果没有再次访问该过期删除的就可能一直堆积在内存里面!从而不会被清除占用大量内存。
所以我们需要另外一种策略来配合使用解决内存占用问题。就是我们的定期过期
2.2 定期过期
所谓定期过期就是我们会每个星期或者每个月去清理一次冰箱把冰箱里面过期的菜全部扔掉。
在Redis中就是我也会定期去把过期的数据删除。
那么究竟多久去清除一次呢我们在讲rehash的时候Redis数据结构扩容源码分析_redis数据结构扩容机制-CSDN博客 有个方法是serverCron,执行频率根据redis.conf中的hz配置
这方法除了做Rehash以外还会做很多其它的事情比如
清理数据库中的过期键值对更新服务器的各类统计信息比如时间、内存占用、数据库占用情况等关闭和清理连接失效的客户端尝试进行持久化操作
我们知道了多久会去扫描一下那么接下来我们需要知道具体是怎么扫的
具体实现流程如下: 1. 定时serverCron方法去执行清理执行频率根据redis.conf中的hz配置 的值 2. 执行清理的时候不是去扫描所有的key而是去扫描所有设置了过期 时间的keyredisDb.expires 3. 如果每次去把所有过期的key都拿过来那么假如过期的key很多就会 很慢所以也不是一次性拿取所有的key 4. 根据hash桶的维度去扫描key扫到20(可配)个key为止。假如第一个桶 是15个key 没有满足20继续扫描第二个桶第二个桶20个key由 于是以hash桶的维度扫描的所以第二个扫到了就会全扫总共扫描 35个key 5. 找到扫描的key里面过期的key并进行删除 6. 如果取了400个空桶或者扫描的删除比例跟扫描的总数超过10%,继续 执行4、5步。 7. 也不能无限的循环循环16次后回去检测时间超过指定时间会跳出。 实现流程图如下: 3.Redis淘汰机制 由于Redis内存是有大小的并且我可能里面的数据都没有过期当快满的时候我又没有过期的数据进行淘汰那么这个时候内存也会满。内存满了之后redis也会放不了新的数据了。
所以我们不得已需要一些策略来解决这个问题来保证可用性。
类比冰箱扔菜
如果我们发现冰箱的菜满了但是冰箱里的菜都是好的那你会咋办?
a. 不放入新的但是可以拿出来吃 -- noeviction
b. 扔掉很久没有吃的 ---LRU
c. 扔掉很少吃的 -----lfu
d. 扔掉即将快过期的 --- ttl
那么在Redis中究竟是怎么处理的呢? noeviction: New values aren’t saved when memory limit is reached. When a database uses replication, this applies to the primary database 默认不淘汰 能读不能写 allkeys-lru: Keeps most recently used keys; removes least recently used (LRU) keys 基于伪LRU算法 在所有的key中去淘汰 allkeys-lfu: Keeps frequently used keys; removes least frequently used (LFU) keys 基于伪LFU算法 在所有的key中去淘汰 volatile-lru: Removes least recently used keys with the expire field set to true . 基于伪LRU算法 在设置了过期时间的key中去淘汰 volatile-lfu: Removes least frequently used keys with the expire field set to true . 基于伪LFU算法 在设置了过期时间的key中去淘汰 allkeys-random: Randomly removes keys to make space for the new data added. 基于随机算法 在所有的key中去淘汰 volatile-random: Randomly removes keys with expire field set to true . 基于随机算法 在设置了过期时间的key中去淘汰 volatile-ttl: Removes least frequently used keys with expire field set to true and the shortest remaining time-to-live (TTL) value. 根 据过期时间来淘汰即将过期的 上述是官网给我们提供了8种不同的策略只要在config配置中配置maxmemory-policy即可指定相关的淘汰策略 # maxmemory-policy noeviction //默认不淘汰数据能读不能写 那么现在我们已经知道了有不同的方式去淘汰那么整个的淘汰流程又是什么呢?LRU跟LFU算法又是什么呢?
3.1 淘汰流程 如上图所示 1.首先我们会有个淘汰池默认大小是16并且里面的数据是末尾淘汰制。 2.每次指令操作的时候自旋会判断当前内存是否满足指令所需要的内存 3.如果当前内存不能满足会从淘汰池中的尾部拿取一个最适合淘汰的数据 3.1 会取样配置 maxmemory-samples从Redis中获取随机获 取到取样的数据 解决一次性读取所有的数据慢的问题 3.2 在取样的数据中根据淘汰算法 找到最适合淘汰的数据 3.3 将最合适的那个数据跟淘汰池中的数据比较是否比淘汰池 中的更适合淘汰如果更适合放入淘汰池 3.4 按照适合的程度进行排序最适合淘汰的放入尾部 4.将需要淘汰的数据从Redis删除并且从淘汰池移除。 3.2 LRU算法
LRU, least Recently Used 翻译过来是最久未使用根据时间轴来走仍很久没用的数据。只要最近有用过我就默认是有效的。
那么它的一个衡量标准是什么呢?事件对不对!根据使用事件从近到远越远的越容易淘汰
实现原理
首先LRU是根据这个对象的访问操作时间来进行淘汰的那我们需要知道这个对象最后的操作访问时间。知道了对象的最后操作访问时间后我们只需要跟当前的系统时间来进行对比就能计算出对象已经多久没有访问了
源码验证
在Redis中对象都会被一个redisObject对象包装这个对象就是我们redis的所有数据结构的对外对象!那么它里面有个字段叫做lru
redisObject对象(server.h文件)
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这个字段的大小为24bit,那么这个字段记录的是对象操作访问时候的秒单位时间的后24bit
long timeMillisSystem.currentTimeMillis();
System.out.println(timeMillis/1000); //获取当前秒
System.out.println(timeMillis/1000 ((124)-1)); //获取秒的后24位
我们知道了这个对象的最后操作访问的时间。如果我们要得到这个对象多久没访问了我们是不是就很简单用我当前的时间-这个对象的访问时间就可以了但是这个访问时间是秒单位时间的后24bit所以也是用当前时间的秒单位的后24bit去减!
假如我们lrulock当前时间的秒单位的最后24bit
那么我们如果要得到这个对象多久没访问 只需要: lrulock - redisObject.lru
但是我们就会发现一个问题: 24bit是有大小限制的最大是24个1那么假如时间一直往前走这个系统时间的最后24bit肯定会变成24个0
举个例子 11111111111111111000000000011111110 假如这个是我当前秒单位的时 间 获取后8位 是 11111110 11111111111111111000000000011111111 获取后8位 是 11111111 11111111111111111000000000100000000 获取后8位 是 00000000 但是比上面的那个二进制肯定要大 所以它有个轮询的概念它如果超过24位又会从0开始!所以我们不能直接用系统时间秒单位的24bit去减对象的lru而是要判断一下怎么判断
举个生活中的例子 我们的月份跟我们的24bit的值是一样的都有最大值只不过月份的最 大值是12。 场景一数据在5月份被操作访问现在是8月份 我们可通过8-53 得 到这个对象3个月没访问 场景二数据在5月份被操作访问现在是3月份 我们可通过 12-53 得 到这个对象10个月没访问 同理
如果redisObject.lrulrulock,直接通过lrulock-redisObject.lru得到这个对象多久没访问
如果redisObject.lrulrulock,通过lrulock 24bit的最大值-redisObject.lru
源码验证 estimateObjectIdleTime方法(evict.c)
unsigned long long estimateObjectIdleTime(robj *o) {//获取秒单位时间的最后24位unsigned long long lruclock LRU_CLOCK();//因为只有24位所有最大的值为2的24次方-1//超过最大值从0开始所以需要判断lruclock当前系统时间跟缓存对象的lru字段的大小if (lruclock o-lru) {//如果lruclockrobj.lru返回lruclock-o-lru再转换单位return (lruclock - o-lru) * LRU_CLOCK_RESOLUTION;} else {//否则采用lruclock (LRU_CLOCK_MAX - o-lru)得到对象的值越小返回的值越大越大越容易被淘汰return (lruclock (LRU_CLOCK_MAX - o-lru)) *LRU_CLOCK_RESOLUTION;}
}
整体流程图:Redis LRU算法实现| ProcessOn免费在线作图,在线流程图,在线思维导图
总结
用lrulock与redisObject.lru进行比较因为Lrulock只获取了当前秒单位时间的后24位所以肯定有个轮询
所以我们会用lrulock跟redisObject.lru进行比较如果lrulockredisObject.lru那么我们用lrulock-redisObject.lru否则lrulock(24bit的最大值-redisObject.lru),得到的lru越小那么返回的数据越大相差越大的越优先会被淘汰!
3.3 LFU算法
LFULeast Frequently Used,翻译成中文就是最不常用的优先淘汰。
不常用它的衡量标准就是次数次数越少的越容易被淘汰
这个实现起来应该也很简单对象被操作访问的时候去记录次数每次操作访问一次就1;淘汰的时候直接去比较这个次数次数越少的越容易淘汰
LFU的时效性问题
但是LFU有个致命的问题那就是时效性问题。何为时效性?就是只考虑数量不考虑时间
举个生活中的例子:
假如去年有个新闻很火比如之前的吴亦凡事件假如点击量是3000W
那么今年又有个新闻刚出来点击量是100次
本来我们应该是要让今年的这个新闻显示出来的吴亦凡虽然去年很火但是由于时间久了我肯定是不希望上热搜的。
但是根据LFU来做的话我们发现淘汰的却是今年的新闻这个明显是不合理的。
导致的问题就是: 新的数据进不去旧的数据出不来
Redis肯定也是知道这个问题的那么Redis是怎么解决的呢?
上面的RedisObject中的lru字段有注释: 它前面16bit代表的是时间后8位代表的是一个数值frequency是频率代表的是这个对象的访问次数。
前16bit时间有什么用呢?大胆猜测应该是跟时效性有关的那么究竟是怎么解决的呢?
我们再来看个生活中的例子 大家应该充过一些会员比如我这个年纪的小时候喜欢充腾讯的黄钻、 绿钻、蓝钻等等。 但是有一个点假如哪天我没充钱了的话或者没有续VIP的时候我这个 钻石等级会随着时间的流失而降低。比如我本来是黄钻V6但是一年不充 钱的话可能就变成了V4。 那么有了这个例子在redis中我们是不是也可以猜测: 这个时间是记录的这个对象有多久没访问了如果超过了多久没访问就去减少对应的次数。
源码验证:
LFUDecrAndReturn方法(evict.c)
unsigned long LFUDecrAndReturn(robj *o) {
//lru字段右移8位得到前面16位的时间
unsigned long ldt o-lru 8;
//lru字段与255进行运算255代表8位的最大值
//得到8位counter值
unsigned long counter o-lru 255;
//如果配置了lfu_decay_time用LFUTimeElapsed(ldt) 除以配置的值
//LFUTimeElapsed(ldt)源码见下
//总的没访问的分钟时间/配置值得到每分钟没访问衰减多少
unsigned long num_periods server.lfu_decay_time ?
LFUTimeElapsed(ldt) / server.lfu_decay_time : 0;
if (num_periods)
//不能减少为负数非负数用couter值减去衰减值
counter (num_periods counter) ? 0 : counter -
num_periods;
return counter;
} 衰减因子的配置 lfu-decay-time 1 //多少分钟没操作访问就去衰减一次 后8bits的次数最大值是255肯定不够但是我们可以让数据达到255很难就是每个数值都是访问了多少次才1那么redis究竟是怎么做的呢?
LFULoglncr方法(evict.c文件)
uint8_t LFULogIncr(uint8_t counter) {//如果已经到最大值255返回255 8位的最大值if (counter 255) return 255;//得到随机数0-1double r (double)rand()/RAND_MAX;//LFU_INIT_VAL表示基数值在server.h配置 默认为5double baseval counter - LFU_INIT_VAL;//如果达不到基数值表示快不行了baseval 0if (baseval 0) baseval 0;//如果快不行了肯定给他加counter//不然按照几率是否加counter同时跟baseval与lfu_log_factor相关//都是在分子所以2个值越大加counter几率越小double p 1.0/(baseval*server.lfu_log_factor1);if (r p) counter;return counter;
} 所以LFU加的逻辑我们可以总结下:
最大只能到255 如果到了255不往上加如果当前次数5count255,那么越往上加的概率越低 lfu-log-factor配置 的值越大添加的几率越小如果小于等于5每次访问必加1因为p1,r是0到1之间的随机数必然小于p
来看一波官方给的压测数据 factor因子与点击量的关系。 文章转载自: http://www.morning.rzcbk.cn.gov.cn.rzcbk.cn http://www.morning.lonlie.com.gov.cn.lonlie.com http://www.morning.fwlch.cn.gov.cn.fwlch.cn http://www.morning.tfpqd.cn.gov.cn.tfpqd.cn http://www.morning.pybqq.cn.gov.cn.pybqq.cn http://www.morning.mrbzq.cn.gov.cn.mrbzq.cn http://www.morning.tkchm.cn.gov.cn.tkchm.cn http://www.morning.hmxb.cn.gov.cn.hmxb.cn http://www.morning.czrcf.cn.gov.cn.czrcf.cn http://www.morning.zczkm.cn.gov.cn.zczkm.cn http://www.morning.kfhm.cn.gov.cn.kfhm.cn http://www.morning.qlrwf.cn.gov.cn.qlrwf.cn http://www.morning.qfmns.cn.gov.cn.qfmns.cn http://www.morning.qkgwx.cn.gov.cn.qkgwx.cn http://www.morning.xfxqj.cn.gov.cn.xfxqj.cn http://www.morning.ptwqf.cn.gov.cn.ptwqf.cn http://www.morning.eronghe.com.gov.cn.eronghe.com http://www.morning.tsmxh.cn.gov.cn.tsmxh.cn http://www.morning.weiwt.com.gov.cn.weiwt.com http://www.morning.yrnyz.cn.gov.cn.yrnyz.cn http://www.morning.mgwpy.cn.gov.cn.mgwpy.cn http://www.morning.fnbtn.cn.gov.cn.fnbtn.cn http://www.morning.tdldh.cn.gov.cn.tdldh.cn http://www.morning.cpfx.cn.gov.cn.cpfx.cn http://www.morning.xnzmc.cn.gov.cn.xnzmc.cn http://www.morning.qdscb.cn.gov.cn.qdscb.cn http://www.morning.glrzr.cn.gov.cn.glrzr.cn http://www.morning.hmdn.cn.gov.cn.hmdn.cn http://www.morning.eshixi.com.gov.cn.eshixi.com http://www.morning.csznh.cn.gov.cn.csznh.cn http://www.morning.hlnys.cn.gov.cn.hlnys.cn http://www.morning.bqwnp.cn.gov.cn.bqwnp.cn http://www.morning.mcpdn.cn.gov.cn.mcpdn.cn http://www.morning.clpfd.cn.gov.cn.clpfd.cn http://www.morning.xgzwj.cn.gov.cn.xgzwj.cn http://www.morning.ktntj.cn.gov.cn.ktntj.cn http://www.morning.fxpyt.cn.gov.cn.fxpyt.cn http://www.morning.wtyqs.cn.gov.cn.wtyqs.cn http://www.morning.lbxhy.cn.gov.cn.lbxhy.cn http://www.morning.hyfrd.cn.gov.cn.hyfrd.cn http://www.morning.rwjh.cn.gov.cn.rwjh.cn http://www.morning.nbmyg.cn.gov.cn.nbmyg.cn http://www.morning.zdwjg.cn.gov.cn.zdwjg.cn http://www.morning.pmxw.cn.gov.cn.pmxw.cn http://www.morning.ngjpt.cn.gov.cn.ngjpt.cn http://www.morning.3jiax.cn.gov.cn.3jiax.cn http://www.morning.ysbhj.cn.gov.cn.ysbhj.cn http://www.morning.hwycs.cn.gov.cn.hwycs.cn http://www.morning.ghwdm.cn.gov.cn.ghwdm.cn http://www.morning.dbnrl.cn.gov.cn.dbnrl.cn http://www.morning.nqpy.cn.gov.cn.nqpy.cn http://www.morning.zwppm.cn.gov.cn.zwppm.cn http://www.morning.lmhh.cn.gov.cn.lmhh.cn http://www.morning.tcpnp.cn.gov.cn.tcpnp.cn http://www.morning.lfdmf.cn.gov.cn.lfdmf.cn http://www.morning.lqypx.cn.gov.cn.lqypx.cn http://www.morning.jrkzk.cn.gov.cn.jrkzk.cn http://www.morning.ykmtz.cn.gov.cn.ykmtz.cn http://www.morning.qkqgj.cn.gov.cn.qkqgj.cn http://www.morning.qfnrx.cn.gov.cn.qfnrx.cn http://www.morning.dddcfr.cn.gov.cn.dddcfr.cn http://www.morning.rwcw.cn.gov.cn.rwcw.cn http://www.morning.ryyjw.cn.gov.cn.ryyjw.cn http://www.morning.jghqc.cn.gov.cn.jghqc.cn http://www.morning.tpmnq.cn.gov.cn.tpmnq.cn http://www.morning.ytrbq.cn.gov.cn.ytrbq.cn http://www.morning.tqwcm.cn.gov.cn.tqwcm.cn http://www.morning.pzqnj.cn.gov.cn.pzqnj.cn http://www.morning.dpplr.cn.gov.cn.dpplr.cn http://www.morning.iknty.cn.gov.cn.iknty.cn http://www.morning.msxhb.cn.gov.cn.msxhb.cn http://www.morning.lfsmf.cn.gov.cn.lfsmf.cn http://www.morning.lhytw.cn.gov.cn.lhytw.cn http://www.morning.gmplp.cn.gov.cn.gmplp.cn http://www.morning.bwznl.cn.gov.cn.bwznl.cn http://www.morning.snrbl.cn.gov.cn.snrbl.cn http://www.morning.plzgt.cn.gov.cn.plzgt.cn http://www.morning.qnklx.cn.gov.cn.qnklx.cn http://www.morning.brwp.cn.gov.cn.brwp.cn http://www.morning.nqbkb.cn.gov.cn.nqbkb.cn