收集现有博文里都集中在描述memcache扩展的缺陷,简单总结如下:
- 高并发下TS不好,不稳定
- 协议支持不完整: memcached扩展基于memcached项目的lib库,能够以极低的成本跟进memcache的更新;并且因为此特点,也支持了更多的mc协议。
- 将数字存储为字符串: 对于强类型,或者是php中"==="这种比较会造成困扰,如set一个test:1, get test会返回"1",与1去做"==="会返回false,造成开发者困惑;
memcached扩展(比对memcache extension)功能上的优化点:
- 提供了setOption api 可以统一设置flag
- 支持二进制协议,提供了更高的性能,低内存、线程安全
- 功能更多:cas 检查并设置
memcache扩展(比对memcached extension)更多的功能点(09年的2.2.0开始支持一致性hash):
-
支持OO和过程两组接口,而memcached扩展只支持OO
-
支持获取or设置key时的failover
其中功能点1不够吸引人,PHP5版本之后,全线切OO编程,因此OO方法足够实现用户的直接使用,关键是功能点2。查阅资料可以得知,当网络抖动or部分服务临时不可用时,memcache扩展会主动的进行rehash,造成数据一致性问题,以一个简单的计数器(限流用)举例:
<?php error_reporting(-1);
//$client = new memcached;
$client = new memcache;
$arr = array(
array("host"=>"127.0.0.1","port"=>11211),
array("host"=>"127.0.0.1","port"=>11212),
);
foreach ($arr as $ele)
{
$client->addServer($ele["host"],$ele["port"],true);
}
$counter = $client->get('counter');
var_dump($counter);
if (empty($counter))
$client->set('counter', 100, 0);
for ($i=0; $i < 100; $i++)
{
try
{
printf("get counter...");
sleep(2);
$counter = $client->get('counter');
printf($counter);
if (false === $counter)
{
printf("connect error");
sleep(1);
continue;
}
if (0 >= $counter)
{
printf("loop end\n");
sleep(1);
exit(1);
}
printf("set counter...\n");
sleep(2);
$client->set('counter', $counter - 1);
}
catch (Exception $e)
{
echo "*";
var_dump($e->getMessage());
continue;
}
}
exit(0);
模拟错误场景:
a. php连接11211和11212集群,counter作为key存在11211实例上;
b. 循环继续,eg:当计数器到90的循环内,在set counter阶段,mcd进程11211失效(以kill来仿真),则将会把counter作为key写入11212节点中(报一个notice) ;
c. 计数器继续递减,eg:当counter为80时,在get counter阶段 11211又启动,所以从11211中拿数据,此时数据为false;在set counter阶段,则将counter=>80写到11211中;
d. 计数器继续递减,eg:当counter为70时,在get counter阶段 11211又失效,则获取counter会拿到上一次切换的点80;
e. 如果使用memcached扩展,则一旦对应的节点失效就会报错,保证通知到运维方,对mc集群进行处理。
由于集群的网络环境不可控,单次操作超时 or 单节点短时间不可用的场景会频繁出现,因此不会使用随机节点rehash的方式来保证系统可用,对数据一致性造成的负面影响过大,因此在memcached扩展中,选择直接返回false,是取舍上收益更大的选择。
针对此错误场景的通用解法应该是:
1. 本地缓存(临时方案)
2. 利用缓存代理(magent)
总结memcache扩展与memcached扩展对比如下表:
|
PECL/MEMCACHE
|
PECL/MEMCACHED
|
---|---|---|
FIRST RELEASE DATE
|
2004-06-08
|
2009-01-29 (beta)
|
ACTIVELY DEVELOPED
|
Yes
|
Yes
|
EXTERNAL DEPENDENCY
|
None
|
libmemcached
|
Features
|
|
|
AUTOMATIC KEY FIXUP
|
Yes
|
No
|
APPEND/PREPEND
|
No
|
Yes
|
AUTOMATIC SERIALZATION
|
Yes
|
Yes
|
BINARY PROTOCOL
|
No
|
Optional
|
CAS
|
No
|
Yes
|
COMPRESSION
|
Yes
|
Yes
|
COMMUNICATION TIMEOUT
|
Connect Only
|
Various Options
|
CONSISTENT HASHING
|
Yes
|
Yes
|
DELAYED GET
|
No
|
Yes
|
MULTI-GET
|
Yes
|
Yes
|
SESSION SUPPORT
|
Yes
|
Yes
|
SET/GET TO A SPECIFIC SERVER
|
No
|
Yes
|
STORES NUMERICS
|
Converted to Strings
|
Yes
|
因此从一个中间件扩展的简单选型可以发现:在做开发框架技术选型时,设计者要保证对中间件扩展的掌握并进行充分测试。根据具体业务场景,进行合理选型。