【问题标题】:php-redis - Is there a way to store PHP object in Redis without serializing it?php-redis - 有没有办法在 Redis 中存储 PHP 对象而不对其进行序列化?
【发布时间】:2014-12-30 08:19:09
【问题描述】:

我正在尝试将用户的请求 URL 存储为键,并将与该键对应的 PHP 对象存储为 Redis 中的值。我尝试了以下方法:

$redisClient = new Redis();
$redisClient->connect('localhost', 6379);
$redisClient->set($_SERVER['REQUEST_URI'], $this->page);
$redisTest = $redisClient->get($_SERVER['REQUEST_URI']);
var_dump($redisTest);

但是,使用此代码,存储在 Redis 中的 URL 键的值是 string 类型,其值等于“对象”,而不是实际的 PHP 对象。有没有办法在不序列化的情况下存储 PHP 对象?

【问题讨论】:

  • 不能序列化有什么原因吗?那将是最直接的方法。
  • 简单的回答,“否”.... Redis 与语言无关,它不知道 PHP 对象是什么,因此在逻辑上不能期望能够保存这样的生物。 ...您需要将对象转换为redis可以保存的某种格式,例如文本表示

标签: php redis phpredis


【解决方案1】:

正如您在Redis data types 中看到的,Redis 仅支持这 5 种数据类型:

  • 字符串
  • 列表
  • 设置
  • 哈希
  • 排序集

因此,没有对象数据类型,因此您无法将对象直接存储为值。您必须先对其进行序列化(或使用 json_encode 函数对其进行 JSON 编码)。

你坚持直接存储对象的序列化有什么问题吗?

更新:根据你在cmets上说的,你可以使用这个answer中指出的方法

所以你可以使用:

$xml = $simpleXmlElem->asXML();

在序列化之前,然后在unserialize()之后,使用以下代码:

$simpleXmlElem = simplexml_load_string($xml);

这样就不用像SimpleXmlElement这样直接序列化PHP内置对象了,不会有问题。

【讨论】:

  • 在 Redis 支持的五种数据类型中,哪种格式是我应该将 PHP 对象序列化为的最佳类型?
  • 最合乎逻辑的类型是String,以便存储它,serialize 函数是最好的选择,而且正如 PHP 手册所说,该函数的目的是使值可存储:php.net/manual/en/function.serialize.php
  • 当我使用 serialize($this->page);我收到此错误:异常:不允许序列化“SimpleXMLElement”
  • PHP 内置对象无法序列化。你想在 Redis 中存储的所有对象都是这种类型的,还是还有其他类型的对象?您也可以考虑将json_encode 作为另一种选择。
  • 我只在 Redis 中存储这种类型的对象。我也尝试json_encode($this->page),但它在 Redis 中存储了空的{}
【解决方案2】:

序列化将是最直接的方式。

另一种方法是json_encode 仅提供稍后重建对象所需的参数。一种方法是使用 PHP 5.4 的 JsonSerialize 接口。您希望使用 jsonSerialize 提取各种属性,然后在您从 Redis 中提取项目时提供将它们传递回您的类的方法。


class MyPage implements JsonSerializable
{

    protected $p1;
    protected $p2;

    /**
     * @param mixed $p1
     */
    public function setP1($p1)
    {
        $this->p1 = $p1;
    }

    /**
     * @param mixed $p2
     */
    public function setP2($p2)
    {
        $this->p2 = $p2;
    }

    /**
     * (PHP 5 &gt;= 5.4.0)<br/>
     * Specify data which should be serialized to JSON
     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
     * @return mixed data which can be serialized by <b>json_encode</b>,
     * which is a value of any type other than a resource.
     */
    public function jsonSerialize()
    {
        return [
            'p1' => $this->p1,
            'p2' => $this->p2,
        ];
    }

}

通过这种方式,您可以轻松地从 JSON 进行重构。您可以添加一个辅助方法来执行此操作,或者只是调用设置器。

【讨论】:

  • 当我使用 json_encode($this->page) 时,它以 '{}' 的形式存储到 Redis 中。你能根据你的答案给出代码示例吗?
  • 它会,因为你还没有改变你的$this-&gt;page 是一个实例的类。如果它是框架的一部分,您可能无法做到。
【解决方案3】:

这是我的做法:

class Cache extends Predis\Client {
    protected $prefix = 'myapp:';

    public function take($key, $value = null, $ttl = null) {
        $value = is_object($value) ? serialize($value) : $value;
        $key   = $this->prefix . $key;
        if (!$this->exists($key)) {
            if (intval($ttl)) {
                $this->setEx($key, $ttl, $value);
            } else {
                $this->set($key, $value);
            }
        }
        return $this->get($key);
    }
}

用法:

$cache = new Cache;

$lorem     = 'Lorem ipsum dolor sit amet';
$loremLong = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';

$cachedLorem          = $cache->take('lorem', $lorem);
$cachedLoremLong      = $cache->take('loremLong', $loremLong);
$cachedLoremTtl       = $cache->take('loremTtl', $lorem, 30);
$cachedLoremGet       = $cache->take('lorem');
$cachedLoremObject    = $cache->take('loremObject', new stdClass);
$cachedLoremObjectTtl = $cache->take('loremObjectTtl', new stdClass, 45);

echo $cachedLorem;
echo $cachedLoremLong;
echo $cachedLoremTtl;
echo $cachedLoremGet;
echo $cachedLoremObject;
echo $cachedLoremObjectTtl;

输出:

Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
O:8:"stdClass":0:{}
O:8:"stdClass":0:{}

【讨论】:

    【解决方案4】:

    Aliweb 答案的补充!

    Redis 支持整数以及 INCR、INCRBY、DECR 和 DECRBY 等操作。

    至于问题:

    仅当不是 String 或 Int 时才进行序列化。序列化是一项昂贵的操作!

    在 GET 和 HGET 上尝试查看是否被序列化:

    '

    private function string_unserialize($str){
            $data = @unserialize($str);
            if ($str === 'b:0;'){
                return 0;
            }elseif($data !== false){
                return $data;
            }else {
                return $str;
            }
        }
    

    '

    【讨论】:

      猜你喜欢
      • 2013-08-08
      • 1970-01-01
      • 2021-02-08
      • 2011-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-16
      • 2021-09-19
      相关资源
      最近更新 更多