【问题标题】:PHP Memcached with Binary Protocol — Junk data returned after `increment()`使用二进制协议的 PHP Memcached — `increment()` 后返回的垃圾数据
【发布时间】:2016-02-06 15:51:36
【问题描述】:

我已经开始使用 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() 调用,我希望返回的值是一个整数。相反,返回的字符串看起来像剩下的垃圾,例如该字符串的ants 部分没有相关性。

其他(可能)相关信息:

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

【问题讨论】:

  • 您是否会同时使用 32 位和 64 位构建的 EC2 都写入这个 memcached 实例?另外,您使用的是哪个版本的 memcached PHP 绑定?

标签: php memcached amazon-elasticache


【解决方案1】:

tl;博士;

这是 PHP 扩展代码中的错误...


我研究了包装 libmemcached 的 PHP 扩展代码和 libmemcached API 代码本身,但我想我已经找到了问题的可能根本原因......

如果您查看 PHP Memcached::increment() 实现,您将在 line 1858 of php_memcached.c 上看到

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

这里的问题是offset 可能是也可能不是 64 位宽。 libmemcached API 告诉我们 memcached_increment_with_initial 函数签名需要 uint64_t 对应 offset 而这里 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"

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

我猜这个问题源于 libmemcached API 对offset 参数在memcached_incrementmemcached_increment_with_initial 之间有两个不同的大小要求

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 代码之间的密钥以某种方式损坏的原因。

【讨论】:

    猜你喜欢
    • 2012-01-21
    • 2013-05-08
    • 1970-01-01
    • 2013-07-13
    • 1970-01-01
    • 1970-01-01
    • 2011-06-23
    • 1970-01-01
    • 2018-10-19
    相关资源
    最近更新 更多