PHP序列化替代方案

4

我正在寻找一个适合APC的缓存键,它可以代表某个对象的一些编译信息,以“对象”作为键。我有一个编译方法,类似于以下内容:

function compile(Obj $obj)
{
    if ($this->cache)
    {
        $cachekey = serialize($obj);

        if ($data = $this->cache->get($obj))
        {
            return $data
        }
    }

    // compute result here

    if ($this->cache)
    {
        $this->cache->set($cachekey, $result);
    }

    return $result;
}

如果不明显的话,$this->cache 是实现具有 getset 方法的接口的一种方式。
有没有更快的方法来创建一个仅对该对象的某些属性唯一的密钥?我可以提取相关部分,但它们仍然是数组,这将与我在第一次使用对象时遇到的序列化问题相同。
从“正确性”的角度来看,序列化可以工作,但它似乎是浪费的(无论是输出键的大小还是计算复杂度)。
编辑:我还想补充一点,如果不明显的话,我将不需要反序列化此对象。 我目前缓存密钥的原始代码实际上是: $cachekey = 'compile.' . sha1(serialize($obj));
编辑2:我正在使用的对象具有以下定义:
class Route
{
    protected $pattern;
    protected $defaults = array();
    protected $requirements = array();
}

Patternrequirements是该方法输出结果会改变的对象的值,因此这些值的哈希必须存在于缓存键中。

另外,有人建议使用uniqid(),这将破坏通用缓存查找键的目的,因为您无法可靠地从相同的信息重新生成相同的ID。

编辑3:我想我没有给出足够的上下文。这里是到目前为止的代码链接:

https://github.com/efritz/minuet/blob/master/src/Minuet/Routing/Router.php#L160

我想我真正想要做的只是避免昂贵的序列化调用(还有sha1,也有点昂贵)。可能我所能做的最好的就是尽量减小我正在序列化的内容的大小...


"计算复杂性"有什么问题吗? - Your Common Sense
它正在运行比我实际需要的更多的循环 ;) 当缓存命中时,serialize目前是唯一比apc_fetch本身花费更多时间的方法。 - efritz
所以你想根据数据项的值检索数据项,或者存储数据项?这没有意义 - 为了检索值,你必须已经知道它是什么! - symcbean
我正在对该项进行额外处理。实际上,我正在生成用于URL路由匹配器的正则表达式,并希望根据模式和正则表达式要求进行存储,但在第一次需要之前不想生成它。 - efritz
1
如果“Obj”有一个唯一的ID,整个事情不是更简单吗? - back2dos
哪个会被重新生成?我没有在请求之间存储路由对象。 - efritz
3个回答

1

一种方法是仅基于用于计算结果的值生成密钥。

这里是一个简单的例子。

function compile(Obj $obj)
{
    if ($this->cache)
    {
        $cachekey = 'Obj-result-' . sha1($obj->pattern . '-' . serialize($obj->requirements));
        // You could even try print_r($obj->requirements, true)
        // or even json_encode($obj->requirements)
        // or implode('-', $obj->requirements)
        // Can't say for sure which is slowest, or fastest.
        if ($data = $this->cache->get($cachekey))
        {
            return $data
        }
    }

    // compute result here
    $result = $obj->x + $obj->y; // irrelevant, and from original answer.

    if ($this->cache)
    {
        $this->cache->set($cachekey, $result);
    }

    return $result;
}

由于您使用了数据数组,因此仍需要将其转换为有意义的键。但是这种方式只序列化对象的一部分,而不是整个对象。看看效果如何。:)


但是 $obj->x 和 $obj->y 实际上将是数组结构,这将需要序列化... 我会对对象结构进行编辑。 - efritz
在你的例子中,$pattern似乎不是一个数组。无论如何,我已经更新了我的答案。 - Pauly
到目前为止,这是最好的解决方案~~ - efritz

1
我建议您使用spl_object_hash函数,它似乎非常适合您的需求。

这个函数在请求之间保持一致的哈希值吗? - efritz
每个当前存在的对象都有一个唯一的字符串,对于每个对象它始终保持不变。请查看PHP手册页面上的注释。 - Tom
1
请注意,该函数并未对对象的内容(属性)进行哈希处理,仅对其内部句柄和处理程序表指针进行处理。每个请求可能在堆栈上有不同的起始点。我正在打印一个对象的哈希值,但它在每个请求中都会发生变化。这在请求之间是无法工作的。 - efritz
此外,如果一个对象在被销毁后仍然占据着同样的内存地址,那么该对象的哈希值可以被重复利用。 - efritz

0

实际上,如果不了解整个系统的工作原理,很难建议任何可行的解决方案。

但是,为什么不在您的对象中简单地添加一个cache_key属性和一个uniqid()值呢?


我不明白一个随机且不可重现的对象如何适用于这个上下文。请参见我上面的编辑,以获取有关该对象的更多信息。 - efritz
你没有理解我的意思。我知道uniqid()是不可复制的。我的意思是将该ID作为签名添加到每个对象中,并将其用作对象的主键。你可以将其与相关对象进行映射,例如[用户1 - obj12328018230]。很抱歉你误解了我的意思,如果你还是无法理解,我再次道歉。 - Rifat
我现在明白了,但我没有将对象存储在数据库中。请检查我的编辑以获取链接。 - efritz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接