Memcached Key Generation从参数到函数


Memcached Key Generation From arguments to a function

这个问题类似于关于java的问题,但我在php中这样做,所以我不认为它是重复的。

我想要一种在调用此函数时生成确定性密钥的方法。该函数的操作应类似于读通缓存。如果密钥存在,则检索数据。如果没有,调用函数存储数据,然后返回。

这是我所拥有的,它是有效的,但我不确定它是否安全,是否足够确定,甚至足够独特,因为我对这些主题完全没有了解。

// $call = function being called $args = arguments to that function
// $force = force cache to bypassed, then updated
public function cachedCall($call,$args = [],$force = false)
{
    $cache = 'App'App::getInstance()->cache;
    $key = md5($call) . md5(serialize($args));
    $res = $cache->get($key);
    if($res === -1 || $force){
        $res = call_user_func_array([$this,$call],$args);
        if(!empty($res) && $res !== false && $res !== 0 && !is_null($res)){
            $cache->set($key,$res,0); //never set empty data in the cache.
        }
    }
    return $res;
}

我的问题只涉及第三行,即密钥的计算。您可以看到它是由被调用的函数和要提供给该函数的参数计算的。我在某些情况下发生过碰撞。我正在寻找改进的方法,使其更有用,哈希一致,但不太可能发生冲突。第三个参数可以忽略,因为它只是强制绕过缓存的一种方式。

如何调用此函数的示例:

$data = $db->cachedCall('getUserByEmail',[$this->email],true);

$data = $db->cachedCall('getCell',['SELECT id FROM foobar WHERE foo=:bar',[':bar'=>55]]);

如果可能的话,我想保证钥匙同时具有一致的长度。

这是因为键在不同的实例中可能是相同的,例如当调用方法cachedCall时具有相同的参数。如我所示,您应该为每个实例共享相同的memcached服务器,这就是发生缓存冲突的原因。

沮丧

正如我所读到的,变量$call将与代码的任何其他部分共享有限的值,因为它将包含包含方法cachedCall的类的方法的名称,这意味着两个不同的调用很容易共享此值

此外,您可以使用空参数数组调用此方法。

因此,在两个不同的实例中很容易调用相同的方法:

cachedCall('methodX', array()); <- From instance A
cachedCall('methodX', array()); <- From instance B

这将把这些内容存储在相同的memcached密钥

解决方案

在方法内部,以某种方式考虑实例名称。例如,您可以使用当前url作为密钥的一部分,或者域名(取决于您的情况):

$key = md5($call) . md5(serialize($args)) . md5($_SERVER['HTTP_HOST']);
$key = md5($call) . md5(serialize($args)) . md5($_SERVER['REQUEST_URI']);

在上面,您可以看到两个示例,说明如何根据您的实例更改memcached键

如果您的参数保证每个查询都是唯一的,并且您会遇到冲突,那么我认为您的代码中可能存在错误。

使用MD5发生冲突的可能性很小。。。

MD5产生冲突之前有多少个随机元素?

如果你看到碰撞,就有问题。PHP序列化数组将按顺序序列化,因此md5(serialize($array_here)应该是安全的。I遇到了一个问题,在尝试传递单个数组时,我没有将调用函数的参数框起来。如果在调用之前args在数组中,那么就没有问题了。