PHP使用Memcache时模拟命名空间及缓存失效问题的解决
缓存命名空间
memcache本身不支持命名空间,但是我们可以利用memcache本身的机制,来模拟命名空间。比如:你要清除一组数据,就需要用到命名空间,来看这样一个例子,说明写在了注释里:
classAction
{
publicfunctionindex()
{
global$mc_wr;
//获取命名空间
$ns_key=$mc_wr->get("foo_namespace_key");
//如果命名空间不存在,则设置一个
if($ns_key===false)$mc_wr->set("foo_namespace_key",time());
$otherParms='select*fromuserLIMIT1';
//根据命名空间生成唯一的key
$my_key="foo_".$ns_key.'_'.md5($otherParms);
//获取当前key下的缓存
$val=$mc_wr->get($my_key);
if(!$val){
$value='wangdekang_'.time();
//缓存不存在则设置缓存600秒,0为随机失效时间,为失效时间添加随机秒数,防止瞬间所有缓存同时失效
$mc_wr->set($my_key,$value,600,0);
}
echo$val;
}
publicfunctionclear_ns()
{
global$mc_wr;
//更新命名空间值,让当前命名空间的所有值失效,memcache自身的缓存失效机制,当缓存不在被访问,会通过LRU失效机制
$mc_wr->set('foo_namespace_key',time());
}
}
memcache缓存失效问题
在大并发的场合,当cache失效时,大量并发同时取不到cache,会同一瞬间去访问db并回设cache,可能会给系统带来潜在的超负荷风险。
解决方法:
方法一
在loaddb之前先add一个mutexkey,mutexkeyadd成功之后再去做加载db,如果add失败则sleep之后重试读取原cache数据。为了防止死锁,mutexkey也需要设置过期时间。伪代码如下
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比实际的memcache
timeout(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();
}
}
}