Memcache mutex设计模式

Memcachemutex设计模式

Monday,Jul26th,2010byTim|Tags:cache,memcache,memcached,mutex

周六的S2Web2.0技术沙龙上介绍了memcache中使用mutex场景(文后要演讲稿),有网友对详情感兴趣,简单介绍如下。

场景

Mutex主要用于有大量并发访问并存在cache过期的场合,如

•首页top10,由数据库加载到memcache缓存n分钟

•微博中名人的contentcache,一旦不存在会大量请求不能命中并加载数据库

•需要执行多个IO操作生成的数据存在cache中,比如查询db多次

问题

在大并发的场合,当cache失效时,大量并发同时取不到cache,会同一瞬间去访问db并回设cache,可能会给系统带来潜在的超负荷风险。我们曾经在线上系统出现过类似故障。

解决方法

方法一

在loaddb之前先add一个mutexkey,mutexkeyadd成功之后再去做加载db,如果add失败则sleep之后重试读取原cache数据。为了防止死锁,mutexkey也需要设置过期时间。伪代码如下

(注:下文伪代码仅供了解思路,可能存在bug,欢迎随时指出。)

if(memcache.get(key)==null){

//3mintimeouttoavoidmutexholdercrash

if(memcache.add(key_mutex,3*60*1000)==true){

value=db.get(key);

memcache.set(key,value);

memcache.delete(key_mutex);

}else{

sleep(50);

retry();

}

}方法二

在value内部设置1个超时值(timeout1),timeout1比实际的memcachetimeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到cache。然后再从数据库加载数据并设置到cache中。伪代码如下

v=memcache.get(key);

if(v==null){

if(memcache.add(key_mutex,3*60*1000)==true){

value=db.get(key);

memcache.set(key,value);

memcache.delete(key_mutex);

}else{

sleep(50);

retry();

}

}else{

if(v.timeout<=now()){

if(memcache.add(key_mutex,3*60*1000)==true){

//extendthetimeoutforotherthreads

v.timeout+=3*60*1000;

memcache.set(key,v,KEY_TIMEOUT*2);

//loadthelatestvaluefromdb

v=db.get(key);

v.timeout=KEY_TIMEOUT;

memcache.set(key,value,KEY_TIMEOUT*2);

memcache.delete(key_mutex);

}else{

sleep(50);

retry();

}

}

}相对于方案一

优点:避免cache失效时刻大量请求获取不到mutex并进行sleep

缺点:代码复杂性增大,因此一般场合用方案一也已经足够。

方案二在MemcachedFAQ中也有详细介绍Howtopreventclobberingupdates,stampedingrequests,并且Brad还介绍了用他另外一个得意的工具Gearman来实现单实例设置cache的方法,见Cachemissstampedes,不过用Gearman来解决就感觉就有点奇技淫巧了。

相关推荐