Memcached的LRU算法

题外话
最近计划对Memcached做一些尝试性的改造,主要是针对Memcached在处理过期数据的时候进行改造,以实现在一个缓存的过期时间达到的时候,可以对该缓存的数据进行一个验证和存储的处理。

这个需求,主要是为了解决MySQL的写入瓶颈,通过延期、合并写入请求来减少MySQL的并发写入量。现在逐渐记录出来和有需要的朋友一起讨论。当然,今天的主要内容是关于LRU的部分。

LRU算法
LRU是Least Recently Used最近最少使用算法。源于操作系统使用的一种算法,对于在内存中但最近又不用的数据块叫做LRU,操作系统会将那些属于LRU的数据移出内存,从而腾出空间来加载另外的数据。

我们来看Memcached(版本 1.4.9)的相关代码:

 
 
/* expires items that are more recent than the oldest_live setting. */
void do_item_flush_expired(void) {
    int i;
    item *iter, *next;
    if (settings.oldest_live == 0)
        return;
    for (i = 0; i < LARGEST_ID; i++) {
        /* The LRU is sorted in decreasing time order, and an item's timestamp
         * is never newer than its last access time, so we only need to walk
         * back until we hit an item older than the oldest_live time.
         * The oldest_live checking will auto-expire the remaining items.
         */
        for (iter = heads[i]; iter != NULL; iter = next) {
            if (iter->time >= settings.oldest_live) {
                next = iter->next;
                if ((iter->it_flags & ITEM_SLABBED) == 0) {
                    do_item_unlink(iter);
                }  
            } else {
                /* We've hit the first old item. Continue to the next queue. */
                break;
            }  
        }  
    }  
}

属于LRU的item是被时间逆序排序了,一个item的失效时间应该是永远比它自身上次被访问的时间大。所以在找到一个item的失效时间比oldest_live这个时间小之前,我们只需要把已经逆序排序的item继续往结尾遍历即可,一旦找到这个item,其他剩余的item也就可以判定为过期了。

如何改动?
我们的思路是让Memcached来作为MySQL的中间承接方,其实LRU算法并不是最适合。列举两种方案:

  1. 继续采用LRU算法,我们需要对Memcached进行改造的是do_item_flush_expired这个函数的执行频度,同时改造do_item_unlink函数来适应需求。
  2. 改用FIFO(先入先出)算法,彻底放弃Memcachd的整个过期处理机制或者另外添加一个FIFO算法序列,来专门为我们的业务需要服务。

为什么写这篇文章?
想写出来和大家一起讨论,看有没有其他的一些更好的用来解决MySQL写入并发的方案。

Tags: MySQL效率, 算法


已有 15 条评论 »
  1. ElmerZhang ElmerZhang

    你的意思是要改造Memcache,当其中某个数据过期被删除前,先将它存入Mysql?
    这样不太好吧。Memcache本身就是一个高速缓存,把他和Mysql绑起来,难免会有问题。假如Memcache满了,然后有大量新数据涌进来,那么Memcache中过期数据往Mysql中转存这一步就有可能会是一个瓶颈,旧数据被清掉的太慢,新数据保存失败。
    我觉得还是单独写个脚本定时将MC中的数据转存入Mysql比较好。
    另外Memcache也可以考虑换成Redis,Redis可以做执久化和replication,防止进程重启或者突然挂掉导致数据丢失。

    1. kimi kimi

      怪我没说清楚,比如我们的业务模型就是社交游戏,社交游戏的用户特点,就是上线之后开始游戏,操作时候十分钟之内关闭游戏下线。我们先定义PHP和MySQL之间有个中间件叫Mcache

      在这个过程中,比如用户刚上线

            
            
      SELECT money,exp FROM user WHERE uid = 1’;

      于是PHP把这个query通过PHP扩展发给了Mcache,Mcache接到这个query,发现没有数据,于是就向MySQL查到数据存下来再返回给PHP;
      几分钟之后,又来了一个操作,加了4个金币

            
            
      UPDATE  user SET money=money+4 WHERE uid = 1’;

      这时候,Mcache接到这个query,发现已经有数据了,就直接在Mcache中对这个数据修改;
      几分钟之后,又来了一个操作,加了10个金币 5点经验

            
            
      UPDATE  user SET money=money+4, exp=exp+5 WHERE uid = 1’;

      这时候,Mcache接到这个query,发现已经有数据了,就直接在Mcache中对这个数据修改;
      又来了一个操作,请求用户当前的金币和经验

            
            
      SELECT money,exp FROM user WHERE uid = 1’;

      这时候,Mcache直接就把最新的query发给PHP去了。

      当30分钟之后,Mcache自动将这条记录update给MySQL。

      这样能减少对MySQL的写入并发,之所以不用NOSQL的方案,是目前NOSQL的方案无法解决回档的问题。而MySQL在热备、增量备份方面的优势是NOSQL方案无法比拟的。你说呢?

      1. ElmerZhang ElmerZhang

        明白了。那么将数据读到缓存,更新缓存,将缓存数据持久化到Mysql,都应该是Mcache这个中间件做的事情,而不是Memcached做的,所以只要把这个中间件做好就OK了呀。
        我提到Redis的意思是让它替代Memcache做缓存,而不是做持久化存储。
        关键数据确实仍然应该用Mysql存储,NoSQL在数据备份、恢复等方面比Mysql还差点。

        1. kimi kimi

          确实,关键性的数据还是得用MySQL存储。为了避免制造重复车轮,计划是在Memcached代码上去做Mcache这个东西。然后开源发出来。有木有兴趣的说~

      2. kaka kaka

        思路确实不错,“写”也可以放内存中执行了。。。

        不过如何应对灾难情况呢?比如:

        “当30分钟之后,Mcache自动将这条记录update给MySQL” ---

        这半小时内,会用内存来存一些金币或经验的操作数据,如果出现了XXX问题造成了机器重启,内存数据丢失又如何应对呢?

        1. kimi kimi

          在这种情况下每个用户会最多丢失半个小时的数据。任何不在计划内的机器重启对MySQL都有可能造成致命性的损害。当然这种不在计划内的机器重启对Mcache也会造成致命性的损害(丢失半个小时的数据)。

          1. kaka kaka

            任何不在计划内的机器重启是可能会造成致命伤害,不过如果是从怎样把这种伤害降到最小的角度来讨论呢??

            比如,若用redis是不是出现XXX情况会好一些?

            redis也是基于内存存储的,不过同时具有以下特点:

            1、redis具有持久化机制,可以定期将内存中的数据持久化到硬盘上。

            2、redis具备binlog功能,可以将所有操作写入日志,当redis出现故障,可依照binlog进行数据恢复。

            1. kimi kimi

              说的有道理,主要是我之前读过Memcached的代码,所以就直接想到用Memcached了。利用Memcached处理过期数据的时候将数据写入MySQL中去。

              我来研究下Redis的代码,看什么时候做这个写入MySQL的操作比较合适。

            2. kaka kaka

              redis这2年很火的,sina微博就是用的redis。。

              不过我的项目没达到那个量级,所以只是看过一些资料,没有实践经验。。希望以后可以看到kimi兄的分享哦~~~ :)

            3. kimi kimi

              之前公司有研究过redis,主要是当时redis需要内存完全大于数据,否则会有性能问题,持久化的特点数据亦不会自动过期,所以就没过多关注,在这点上,Membase就完全超过redis。

            4. kaka kaka

              Membase 能搜到的资料还是比较少的,在你的一篇blog上看到过介绍,貌似优点很多。。。

              想请教下kimi兄在生产环境实践中,有无遇到什么问题或者瓶颈??如果现在一个项目想选择nosql的缓存方案,

              是否是首推Membase??

            5. kimi kimi

              看数据量吧,目前是各有优缺点,我们是在生产环境采用的Membase,放弃Tokyotyrant、Redis也是有原因的,推荐采用Membase,但是Membase的资料却是太少了。

            6. tcl tcl

              1.个人觉得哪有完美的方案,没有。都是看实际需求,可以不可以满足。出现错误情况能不能忍受,能忍受到什么程度的错误
              2.当时做游戏的时候也是将数据写到memcached,然后后台有个程序定时的从memcached中取数据,写到mysql,往memcached中的写数据的时候设置永不过期,后台程序取走数据,写到mysql的时候再设置memcached中数据过期。但是数据都放到了memcached中了。万一memcached挂了,就歇菜了。这块是不是写的时候写到两个memcached中,还是说写到一个memcached集群里面。

  2. sogo sogo

    cache 除了解决高并发方面的问题,想请教一下,读cache 与读取mysql 的时间上面,那个更快,如果评测?

  3. freeacger freeacger

    博主.你的代码插件用的是啥,能否告知?


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值