带有二进制协议的PHP Memcached – 在`increment()`之后返回的垃圾数据

我已经开始使用PHP Memcached客户端的increment()方法,并切换到二进制协议.显然,increment()is only supported on the binary protocol.偶尔,我看到垃圾结果从增量键返回.例如:

$memcached = new \Memcached();
$memcached->setoption(\Memcached::OPT_BINARY_PROTOCOL, TRUE);

$this->cache->increment($key,1,1);

$this->cache->get($key);

输出

"1\u0000ants1 0 1\r\n1\r\n1\r\n25\r"

鉴于密钥在它首先递增之前不存在,并且初始值1被赋予increment()调用,我期望返回的值是整数.相反,返回的字符串看起来像左边的垃圾,例如该字符串的蚂蚁部分没有相关性.

其他(可能)相关信息:

>我在一系列不同的钥匙上看到了这一点
>我们的Memcached服务器是AWS Elasticache实例
>使用相同缓存节点的其他客户端未使用二进制协议.
>所有客户端都运行相同的操作系统(CentOS),PHP和Memcached版本.

解决方法:

TL;博士;

这是PHP扩展代码中的一个错误……

我挖掘了包含libmemcached和libmemcached API代码本身的PHP扩展代码,但我想我已经找到了问题的潜在根本原因……

如果你看一下你在line 1858 of php_memcached.c上看到的PHP Memcached :: increment()实现

status = memcached_increment_with_initial(m_obj->memc, key, key_len, (unsigned int)offset, initial, expiry, &value);

这里的问题是偏移可能是64位宽,也可能不是. libmemcached API告诉我们memcached_increment_with_initial函数签名期望uint64_t用于偏移,而这里offset被声明为long,然后转换为unsigned int.

所以,如果我们做这样的事……

$memcached = new memcached;
$memcached->addServer('127.0.0.1','11211');
$memcached->setoption(\Memcached::OPT_BINARY_PROTOCOL, TRUE);

$memcached->delete('foo'); // remove the key if it already exists
$memcached->increment('foo',1,1);

var_dump($memcached->get('foo'));

你会看到……

string(22) "8589934592
"

作为该脚本的输出.请注意,这仅在该memcached服务器上尚不存在密钥foo时才有效.还要注意该字符串的长度为22个字符,显然它不应该在那附近.

如果你看一下该字符串的十六进制表示….

 var_dump(bin2hex($memcached->get('foo')));

结果是最后清除垃圾……

 string(44) "38353839393334353932000d0a000000000000000000"

正在存储的对象在演员之间明显被破坏.因此,您可能最终得到与我相同的结果,或者您可能最终得到完全破碎的数据,如上所述.它取决于强制转换如何影响当时存储的内存块(这里将进入未定义的行为).此外,唯一看似根本原因是使用带增量的初始值(在此之后使用增量不会证明该问题或密钥已存在).

我猜这个问题源于这样一个事实,即libmemcached API对memcached_increment和memcached_increment_with_initial之间的offset参数有两个不同的大小要求.

memcached_increment(memcached_st *ptr, const char *key, size_t key_length, uint32_t offset, uint64_t *value)

前者使用uint32_t,而后者使用uint64_t,PHP的扩展代码将两者都转换为unsigned int,这几乎等同于uint32_t.

offset参数宽度的这种差异可能是导致密钥在调用PHP扩展代码和API代码之间以某种方式被破坏的原因.

相关文章

统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
前言 之前做了微信登录,所以总结一下微信授权登录并获取用户...
FastAdmin是我第一个接触的后台管理系统框架。FastAdmin是一...
之前公司需要一个内部的通讯软件,就叫我做一个。通讯软件嘛...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...