设为首页 收藏本站
查看: 888|回复: 0

[经验分享] 探究redis和memcached的 LRU算法--------redis的LRU的实现

[复制链接]

尚未签到

发表于 2015-11-18 14:22:28 | 显示全部楼层 |阅读模式
  一直对这redis和memcached的两个开源缓存系统的LRU算法感兴趣。今天就打算总结一下这两个LRU算法的实现和区别。
  首先要知道什么是LRU算法:LRU是Least Recently Used 近期最少使用算法。相关的资料网上一大堆。http://en.wikipedia.org/wiki/Cache_algorithms#LRU

  
redis的六种策略


rewriteConfigEnumOption(state,"maxmemory-policy",server.maxmemory_policy,
"volatile-lru", REDIS_MAXMEMORY_VOLATILE_LRU,
"allkeys-lru", REDIS_MAXMEMORY_ALLKEYS_LRU,
"volatile-random", REDIS_MAXMEMORY_VOLATILE_RANDOM,
"allkeys-random", REDIS_MAXMEMORY_ALLKEYS_RANDOM,
"volatile-ttl", REDIS_MAXMEMORY_VOLATILE_TTL,
"noeviction", REDIS_MAXMEMORY_NO_EVICTION,
NULL, REDIS_DEFAULT_MAXMEMORY_POLICY);


  • volatile-lru:从已设置过期时间的数据集(server.db.expires)中挑选最近最少使用的数据淘汰
  • volatile-ttl:从已设置过期时间的数据集(server.db.expires)中挑选将要过期的数据淘汰
  • volatile-random:从已设置过期时间的数据集(server.db.expires)中任意选择数据淘汰
  • allkeys-lru:从数据集(server.db.dict)中挑选最近最少使用的数据淘汰
  • allkeys-random:从数据集(server.db.dict)中任意选择数据淘汰
  • no-enviction(驱逐):禁止驱逐数据




  而lru算法经常是被用在缓存系统,就比如我们的memcached和redis上。
  首先介绍一下redis的。我去最近刚发布的redis3.0上搜索一下代码,通过它的代码实现和如何实现lru来讲解。
  /* The actual Redis Object */
#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 {
unsigned type:4;
unsigned encoding:4;
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
int refcount;
void *ptr;
} robj;
/* Macro used to obtain the current LRU clock.
* If the current resolution is lower than the frequency we refresh the
* LRU clock (as it should be in production servers) we return the
* precomputed value, otherwise we need to resort to a function call. */
#define LRU_CLOCK() ((1000/server.hz <= REDIS_LRU_CLOCK_RESOLUTION) ? server.lruclock : getLRUClock())
  unsigned int getLRUClock(void) {
return (mstime()/REDIS_LRU_CLOCK_RESOLUTION) & REDIS_LRU_CLOCK_MAX;
}





定义相关的一些lru的一些常量,并将   unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */

讲object的成员加入一个24位长度的lru成员。
  


  


我们发现使用最简单粗暴的方式,就是和ALL -key机制是一样,随机选出16个。通过过期时间对pool内存数据进行淘汰。
    /* 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;
}
}
}
}

通过lru来淘汰,返回lru时间,然后通过当前lru比较
  /* Given an object returns the min number of milliseconds the object was never
* requested, using an approximated LRU algorithm. */
unsigned long long estimateObjectIdleTime(robj *o) {
unsigned long long lruclock = LRU_CLOCK();
if (lruclock >= o->lru) {
return (lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION;
} else {
return (lruclock + (REDIS_LRU_CLOCK_MAX - o->lru)) *
REDIS_LRU_CLOCK_RESOLUTION;
}
}
  



在servercron会定时更新起LRU的时间。随机找到maxmemeory中的key,然后根据lru数&#20540;进行删除。
因为unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) {


这个方法是通过随机得来的数据。



总结:
redis只是每个Object维护一个相对的时间,淘汰时,随机取3个或者更多的,找到最老的进行淘汰.这样节省了双链表的指针开销,读时还不用加锁.虽不能保证一定淘汰最老的,但倾向于淘汰偏老的对象,
经过我们线上的实测:和标准的LRU对比,命中率的损失非常小, 效果不错。





更多文章,欢迎访问
http://blog.iyunv.com/wallwind







  


  


  


  




  


  


  








  
  


  





版权声明:本文为博主原创文章,未经博主允许不得转载。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-140780-1-1.html 上篇帖子: linux下memcached安装 下篇帖子: simple-spring-memcached简介
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表