诺亚方舟

沉淀

利用memcache实现原子计数器

工作中有不少业务场景中需要用到计数器,在处理计数器有效性的时候不得不考虑一个问题是并发时如何保证计数的有效准确,这里利用mc的一些原子操作(即操作某个项时会给它带上锁,别的客户端进程影响不到它)下面记录两种自己考虑到的方案。

方案一:利用increment与add

对于memcache,incr是一个原子性的操作,因此可以利用incr与add搭配(这里add压测了下,感觉也像是原子操作,具体不能判断,后面学习到再纠正),php代码实现:

1
2
3
4
5
6
7
$key = 'c';
$mc = new Memcache;
$mc->addServer('127.0.0.1', '11211');
if(!($times = $mc->increment($key, 1))){
    $times = $mc->add($key, 1);
    $times = ($times)?$times:$mc->increment($key, 1);
}

纠正方案:

1
2
3
4
5
6
7
8
9
10
11
<span style="color: #ff0000;">$key = 'c';
$mc = new Memcache;
$mc-&gt;addServer('127.0.0.1', '11211');
if(!($times = $mc-&gt;increment($key, 1))){
    $times = $mc-&gt;add($key, 1, MEMCACHE_COMPRESSED, 30);//30s
    $times = ($times)?$times:$mc-&gt;increment($key, 1);
}
注:这里也可以利用replace,这两个方法的原型分别是:
replace('key','value','is_compress','time');
add('key','value','is_compress','time');
</span>

 

方案二,利用gets与cas,这对组合的原理是在进行gets时会获取item的一个票根,进行写入即cas时会带上票根验证,如果票根与当前item票根不一致说明已被其他客户端写入,再次写入会变成脏数据,需要放弃写入,重写获取gets值。这种方式的操作有些麻烦,处理逻辑时需要一个循环多次尝试gets与cas来保证并发问题的解决,另外值得一提的是php扩展memcached才支持这两个方法,memcache不支持。php实现如下:

1
2
3
4
5
6
7
8
$mc = new Memcached;
$mc-&gt;addServer('127.0.0.1', '11211');
do{
	$key = 'c';
	$times = $mc-&gt;get($key, null, $token);
	$times ++;
	$ttl = 60;
}while($mc-&gt;cas($token, $key, $times, $ttl));

以上的两种实现方式是自己想法,可能有一定弊端,个人觉得计算器最严谨的方式还是用队列来实现,取一个扔出一个。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>