使用标准php库使许多memcache密钥无效的最佳方法?

Jon*_*eig 6 php memcached caching

我有一个包含文件的数据库,可以在多个服务器上搜索,浏览和拥有多个副本.

我缓存搜索,浏览页面和服务器位置(网址).假设我删除了一个文件,有什么方法可以使所有搜索无效,浏览此文件的数据和网址?或者如果文件服务器出现故障,我需要使指向此服务器的所有URL无效?

基本上我正在寻找类似于memcache-tags的东西,但是使用标准的memcache和php组件.(无需更改Web服务器本身的任何内容).在键之间我需要某种多对多的关系(一个服务器有很多文件,一个文件有多个服务器),但似乎无法找到实现这一目标的好方法.在某些情况下,过时的缓存是可接受的(次要更新等),但在某些情况下,它不是(通常是删除和服务器关闭)我需要使包含对它的引用的所有缓存项无效.

我看过的一些方法:

命名空间:

$ns_key = $memcache->get("foo_namespace_key");
// if not set, initialize it
if($ns_key===false) $memcache->set("foo_namespace_key", rand(1, 10000));
// cleverly use the ns_key
$my_key = "foo_".$ns_key."_12345";
$my_val = $memcache->get($my_key);

//To clear the namespace do:
$memcache->increment("foo_namespace_key");
Run Code Online (Sandbox Code Playgroud)
  • 将缓存键限制为单个命名空间

项目缓存方法:

$files = array('file1','file2');
// Cache all files as single entries
foreach ($files as $file) {
  $memcache->set($file.'_key');
}
$search = array('file1_key','file2_key');
// Retrieve all items found by search (typically cached as file ids)
foreach ($search as $item) {
  $memcache->get($item);
}
Run Code Online (Sandbox Code Playgroud)
  • 如果文件服务器关闭会出现问题,并且包含此服务器的URL的所有密钥都应该无效(EG将需要大量的小缓存项,这反过来需要大量的缓存请求) - 打破任何缓存的机会完整对象和结果集

标签实施:

class KeyEnabled_Memcached extends Zend_Cache_Backend_Memcached
{

    private function getTagListId()
    {
        return "MyTagArrayCacheKey";
    }

    private function getTags()
    {
        if(!$tags = $this->_memcache->get($this->getTagListId()))
        {
            $tags = array();
        }
        return $tags;
    }

    private function saveTags($id, $tags)
    {
        // First get the tags
        $siteTags = $this->getTags();

        foreach($tags as $tag)
        {
            $siteTags[$tag][] = $id;
        }
        $this->_memcache->set($this->getTagListId(), $siteTags);        
    }

    private function getItemsByTag($tag)
    {
        $siteTags = $this->_memcache->get($this->getTagListId());
        return isset($siteTags[$tag]) ? $siteTags[$tag] : false;
    }

    /**
     * Save some string datas into a cache record
     *
     * Note : $data is always "string" (serialization is done by the
     * core not by the backend)
     *
     * @param  string $data             Datas to cache
     * @param  string $id               Cache id
     * @param  array  $tags             Array of strings, the cache record will be tagged by each string entry
     * @param  int    $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
     * @return boolean True if no problem
     */
    public function save($data, $id, $tags = array(), $specificLifetime = false)
    {
        $lifetime = $this->getLifetime($specificLifetime);
        if ($this->_options['compression']) {
            $flag = MEMCACHE_COMPRESSED;
        } else {
            $flag = 0;
        }
        $result = $this->_memcache->set($id, array($data, time()), $flag, $lifetime);
        if (count($tags) > 0) {
            $this->saveTags($id, $tags);
        }
        return $result;
    }

    /**
     * Clean some cache records
     *
     * Available modes are :
     * 'all' (default)  => remove all cache entries ($tags is not used)
     * 'old'            => remove too old cache entries ($tags is not used)
     * 'matchingTag'    => remove cache entries matching all given tags
     *                     ($tags can be an array of strings or a single string)
     * 'notMatchingTag' => remove cache entries not matching one of the given tags
     *                     ($tags can be an array of strings or a single string)
     *
     * @param  string $mode Clean mode
     * @param  array  $tags Array of tags
     * @return boolean True if no problem
     */
    public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
    {
        if ($mode==Zend_Cache::CLEANING_MODE_ALL) {
            return $this->_memcache->flush();
        }
        if ($mode==Zend_Cache::CLEANING_MODE_OLD) {
            $this->_log("Zend_Cache_Backend_Memcached::clean() : CLEANING_MODE_OLD is unsupported by the Memcached backend");
        }
        if ($mode==Zend_Cache::CLEANING_MODE_MATCHING_TAG) {
            $siteTags = $newTags = $this->getTags();
            if(count($siteTags))
            {
                foreach($tags as $tag)
                {
                    if(isset($siteTags[$tag]))
                    {
                        foreach($siteTags[$tag] as $item)
                        {
                            // We call delete directly here because the ID in the cache is already specific for this site
                            $this->_memcache->delete($item);
                        }
                        unset($newTags[$tag]);
                    }
                }
                $this->_memcache->set($this->getTagListId(),$newTags);
            }
        }
        if ($mode==Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG) {
            $siteTags = $newTags = $this->getTags();
            if(count($siteTags))
            {
                foreach($siteTags as $siteTag => $items)
                {
                    if(array_search($siteTag,$tags) === false)
                    {
                        foreach($items as $item)
                        {
                            $this->_memcache->delete($item);
                        }
                        unset($newTags[$siteTag]);
                    }
                }
                $this->_memcache->set($this->getTagListId(),$newTags);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
  • 由于内部memcache密钥丢失,无法控制哪些密钥无效,可以删除标记密钥,从而使大量实际有效密钥无效(这仍然存在)
  • 写并发问题

两步缓存系统:

// Having one slow, and one fast cache mechanism where the slow cache is reliable storage  containing a copy of tag versions 
$cache_using_file['tag1'] = 'version1';
$cache_using_memcache['key'] = array('data' = 'abc', 'tags' => array('tag1' => 'version1');
Run Code Online (Sandbox Code Playgroud)
  • 使用磁盘/ mysql等缓慢缓存的潜在瓶颈
  • 写并发问题

Jon*_*eig 0

看到这里的评论,它解释了逐出现有键的逻辑,我相信标签可以通过以下提到的版本标志方法可靠地实现:PHP memcache设计模式

我实际上已经实现了这个逻辑一次,但由于内存缓存在元素过期之前将其逐出,因此将其丢弃为不可靠。您可以在这里找到我的初步实现。然而,我相信这是一个可靠的标签模式,因为:

  • 在缓存对象检索时,对象被移动到 LRU 堆栈的顶部
  • 所有标签/标志都在缓存对象之后检索
  • 如果一个标志被逐出,则任何包含该标志的项目都将在之前被逐出
  • 增加标志会在同一操作中返回新值,从而避免写入并发。如果第二个线程在写入缓存对象之前递增相同的标志,则根据定义它已经无效

如果我错了请纠正我!:-)