cache中的key mutex问题解决及延伸应用

            [文章作者:孙立 链接:http://www.cnblogs.com/sunli/ 更新时间:2010-07-27]

         上周六去参加了csdn举办的TUP活动,最后一场的Tim Yang讲的《微博cache设计谈》,个人觉得讲得非常好和非常到位,其中有两点非常感同身受,就是内网流量问题和cache的key mutex问题导致大量请求穿透到db。后他又写了一篇博客《Memcache mutex设计模式》阐述这个问题。关于cache的key mutex问题在我的开发中叶碰到过很多,这里也就谈下我的解决办法。问题的产生如下图:

   

                                         图一:key mutex问题

         我这里指的cache不局限于memcached,事实上,key mutex在很多场合都是需要的,比如mysql中的innodb中说的机遇记录集的锁也可以当成key mutex。

      http反向代理服务器cache的key mutex问题

         我在09年为一个当时性能不是很高,但是短时间又难于优化的一个系统开发了一个反向代理服务器ICProxy(类似于squid,varnish等,只是增加了一些自己的业务逻辑)来提高系统的性能。但是很快问题就来了,

在访问高峰期,比如

  • 一个热门的新闻页面,设置缓存5分钟
  • 5分钟后,会有大量的请求在同一时间得到缓存失效的标识
  • 大量请求并同时都穿透到后端web服务器请求加载数据到缓存,造成后端频繁宕机
这就是所说的key mutex问题,没有对统一资源的请求加锁来限制穿透访问,由于ICProxy是java开发的,可以很方便的使用锁来进行控制。这里就不贴源代码了,我相信你一定知道怎么实现。
      Memcached的key mutex问题
     memcached的key mutex问题在Tim Yang的博客《Memcache mutex设计模式》已经把产生的场景说得很清楚,也提到了两种解决办法。问题的解决如下图:
 
 
     在以前的系统,我们也碰到过这个问题,因此我开发了一个phplock项目,因为我们在前段主要使用php,所以这里我再为php增加一种方法
     方法三:
    
代码<?php
//获取memcached实例
$cache = IFengMem::getInstance ( 'bbsmemcache' );
//获取缓存中的数据
$result = $cache->get ( $cacheKey );
if ($result === false) {
require_once 'class/class.phplock.php';
//初始化锁,$cacheKey必须传入,用于内部的hash
$lock = new PHPLock ( 'locks/', $cacheKey );
$lock->startLock ();
//加锁
$lock->Lock ();
//进入锁后,查询一次缓存看是否已经有数据了
$result = $cache->get ( $cacheKey );
//如果还是没有数据,表示是自己最先进去到锁的,查询数据库并加载到缓存
if ($result === false) {
$query = $db->query ( $sql );
$result = $db->fetchArray ( $query );
$cache->set ( $cacheKey, $result, $cacheTime );
}
//释放锁
$lock->unlock ();
$lock->endLock ();
}
?>

        phplock你可以在 http://code.google.com/p/phplock/获取到。

        在php应用中,特别是基于nginx等fast-cgi的应用,你应该尽量优化你的查询,让其尽量的快,缩短锁等待,不然不管是穿透到后端db还是在锁等待,都会导致系统负载的急剧上升。你因该根据你系统的业务情况,看是否可以后台线程来更新缓存。

      注意

      在以上的场景中,锁的利用实际上是使用了分离锁,也即是把大量需要进行锁的资源hash到指定的数目锁上,避免过多的锁,也即不是直接进行全局的lock(),这样会降低系统的吞吐量,在phplock中,有个$hashNum可以设置可以hash桶数目,也即可以同时进行访问的key的数量,这样也避免了大量的不同的key的穿透到数据库,phplock是单机版本的,对于多台机器使用的话,可能会有最大机器数的请求穿透的服务器,如果你的web服务器不是非常多的话,也不会有什么问题。

    key mutex的延伸应用

     在很多系统中,我们要对某一个资源进行一个比较耗时的操作时,比如图片下载,网络请求,数据查询,搜索等,由于在大并发下,有大量的请求到同一个资源和不同的资源,那么key mutex的场景都是适用的。当你要开发一个nosql数据库的时候,你必须要考虑这个问题。

     比如你要开发一个类似于memcached的increment的数据累加器,

     你可能不希望

 

lock.lock();
int num=storage.get(key);
storage.set(key,num
+1);
lock.unlock();

 

而是如下的方式来提高吞吐量

 

lock.lock(key);
int num=storage.get(key);
storage.set(key,num
+1);
lock.unlock(key);

 

这个比较类似于mysql中的mysiam和innodb的表锁和行锁的概念,显然行锁的并发能力比表锁高很多。

posted @ 2010-07-27 10:34 草屋主人 阅读(...) 评论(...) 编辑 收藏