php - 注意通过 Redis 在 CakePHP 中反序列化错误

标签 php cakephp serialization redis cakephp-2.5

更新:

根据下面接受的答案的建议,我测试了读取负数:

$negativeInt = -1;
Cache::write('my-test-count', $negativeInt, 'short');
$readVal = Cache::read('my-test-count', 'short');
debug($readVal);
exit;

当尝试读取任何负数时,反序列化错误始终重现。它现在是一个公认的错误,我认为它会在 2.8.1 中得到解决


原始问题:

我一直收到此错误,但不知道为什么,甚至不知道如何进一步解决问题。

只有当 Cache::read() 返回 false 时,抛出错误的行才会被命中。但是,如果我没有在它前面加上 @,该行本身会引发反序列化错误。

问题: 如何在不定期收到反序列化通知的情况下可靠地使用 Redis 进行计数?如果 key 中的数据是“坏的”,我怎么能在不通过执行 ::read 得到通知的情况下知道这一点。我已尝试确保我的数据是 (int)(见下文),但这似乎没有帮助。

Notice (8): unserialize(): Error at offset 0 of 2 bytes [APP/Vendor/pear-pear.cakephp.org/CakePHP/Cake/Cache/Engine/RedisEngine.php, line 136]

检查错误后:

> unserialize - [internal], line ??
> RedisEngine::read() - APP/Vendor/pear-pear.cakephp.org/CakePHP/Cake/Cache/Engine/RedisEngine.php, line 136
> Cache::read() - APP/Vendor/pear-pear.cakephp.org/CakePHP/Cake/Cache/Cache.php, line 358
> Cache::remember() - APP/Vendor/pear-pear.cakephp.org/CakePHP/Cake/Cache/Cache.php, line 567
> Item::getCount() - APP/Model/Item.php, line 812

它似乎来自这个函数:

public function getCount($id) {
    $model = $this;

    // here is where the Cache::read() and debug are in the update below

    return Cache::remember('item' . $id. '_count', function() use ($model, $id) {
        $count = $model->find('count', array(
            'conditions' => array(
                $model->alias . '.status' => 1,
                $model->alias . '.id' => $id
            )
        ));
        return ($count === false) ? 0 : (int)$count;
    }, 'my_counts'); // THIS IS LINE 812
}

public function decrementCount($id, $offset = 1) {
    if(empty($id)) return false;
    $count = @Cache::read('item' . $id . '_count', 'my_counts');
    if($count === false) {
        $this->getCount($id);
    } else {
        Cache::decrement('item' . $id . '_count', $offset, 'my_counts');
    }
}

public function incrementCount($id, $offset = 1) {
    if(empty($id)) return false;
    $count = @Cache::read('item' . $id. '_count', 'my_counts');
    if($count === false) {
        $this->getCount($id);
    } else {
        Cache::increment('item' . $id. '_count', $offset, 'my_counts');
    }
}

更新:

此函数在一个循环中运行(通过 1-20 个项目)。当我在 Cache::remember(...:

之前添加以下内容时
$toReturn = Cache::read('item' . $id. '_count', 'my_counts');
debug($toReturn);

它给出了这个:

  1. 调试:(整数)0
  2. 调试:(整数)0
  3. 通知 (8):unserialize(): Error at...(来自 Cache::read)
  4. 调试:错误
  5. 通知 (8):unserialize(): Error at...(来自 Cache::remember
  6. 调试:(整数)0
  7. 通知 (8):unserialize(): Error at...(来自 Cache::read)
  8. 调试:错误
  9. 通知 (8):unserialize(): Error at...(来自 Cache::remember
  10. 0
  11. 3
  12. 1
  13. 1 ...

最佳答案

这似乎是一个核心错误。我也许能够回答为什么会出现此问题。

我猜测这个问题是由 int(-1) 引起的。 写入数据时,如果数据是整数,RedisEngine 不会序列化数据。 因此,int(-1)将在不调用serialize()的情况下被保存。

public function write($key, $value, $duration) {
    if (!is_int($value)) {
        $value = serialize($value);
    }
    ...
}

但在读取数据时,实现似乎是不对称的。我不太了解 Redis,但我猜测 Redisint(-1) 存储为 string(-1) 。由于 ctype_digit("-1") 返回 falsestring(-1) 将被错误地反序列化。

public function read($key) {
    $value = $this->_Redis->get($key);
    if (ctype_digit($value)) { // ctype_digit("-1") === false
        $value = (int)$value;
    }
    if ($value !== false && is_string($value)) { // is_string("-1") === true
        $value = unserialize($value);
    }
    ...
}

因此,您将看到“通知 (8):unserialize():在 2 个字节的偏移量 0 处出错”。 string(-1) 的大小是 2 个字节。

在 github 上打开一个问题怎么样,如果你可以重现编写 int(-1) 并阅读它会引发一个通知。

除此之外,解决方法是停止使用 Cache::decrement()。在竞争条件下,该方法将存储 int(-1)。 如果它不是重要数据,您可以使用 Cache::write() 代替。例如:

$count = Cache::read('item' . $id. '_count', 'my_counts');
if ($count === false || 0 > $count - $offset) {
    $this->getCount($id);
} else {
    Cache::write('item' . $id. '_count', $count - $offset, 'my_counts');
}

但是如果是重要的数据,那么可能需要实现独占锁/解锁。

感谢您阅读本文,如有错误,请见谅。

关于php - 注意通过 Redis 在 CakePHP 中反序列化错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34936268/

相关文章:

serialization - ColdFusion:有人使用WDDX吗?

php - laravel 包不安装照明/支持

bash - CakePHP 3.x 如何使用控制台清除 View 缓存?

cakephp - 如何通过cakephp迁移在数据库表中添加一个字段?

scala - 如何在反序列化期间初始化 transient 字段?

java - Jersey JSON/JAXB 忽略或看不到 POJO 中的 "some"公共(public)属性

php - 如何制作外键

php - 如何按数组定义的特定顺序对大量项目进行排序?

php - 如何在 PHP 中获取图像质量

php - 使用 cakephp 对连接表进行分页