在create / update / delete

时间:2015-10-15 04:11:32

标签: php symfony caching doctrine-orm memcached

我在Symfony 2.7应用程序中使用Doctrine2 memcache (不是memcache D )。这是我的配置:

doctrine:
    orm:
        metadata_cache_driver: memcache
        result_cache_driver: memcache
        query_cache_driver: memcache

我正在尝试删除实体create / update / delete上的相应缓存项。由于memcache不支持使用前缀删除,我找不到一个简单的解决方案来删除实体在其CUD操作上的某些缓存项。假设我有一个Property实体,我想在Property创建/更新/删除时删除与Property相关的所有缓存。 (我相信在这种情况下deleteAll()不是一个好主意)

Deleting using namespace could be a workaround,但我认为namespace can only be set on the cache driver,而不是特定的实体或存储库。 如何区分每个实体的名称空间?

所以,我现在invalidating the result cache using onFlush event(这是我找到的唯一方法,这篇文章在2012年太旧了。最近有什么更好的解决方案吗?)。该解决方案的一个缺点是为每个查询定义每个缓存ID,并在服务类CacheInvalidator中定义所有这些,例如

namespace MyBundle\Listener\Event;
// ...

class CacheInvalidator
{
    //...

    protected function getCacheIds()
    {
        return array(
            'MyBundle\Entity\BlogPost' => array(
                array(
                    'id' => 'posts_for_user_%d',
                    'vars' => array(
                        array(
                            'value' => 'getUser',
                            'type' => 'method',
                        ),
                    ),
                    'change' => 'any',
                ),
            ),
            // Add more entities etc here
        );
    }
}

文章作者使用缓存标识posts_for_user_%d给出了一个非常简单的示例。使用这种简单的查询很好。问题是我有很多查询使用了很多动态查询参数。例如,我在Property存储库中有以下方法,可以使用各种选项调用:

public function findRecommendations($options = null)
{
    if (isset($options['limit'])) {
        $limit = $options['limit'];
    } elseif (is_numeric($options)) {
        $limit = $options;
    } else {
        $limit = null;
    }

    $qb = $this->createQueryBuilder('p');

    $qb->where('p.recommend_flag = :recommend_flag');
    $qb->setParameter('recommend_flag', 1);
    $qb->andWhere('DATE(p.expired_at) > :now');
    $qb->setParameter('now', date('Y-m-d'));

    $resultCacheParams = array(
        'type'  => array('%d', '%s'),
        'value' => array(1, date('Ymd'))
    );

    if ($limit) {
        $qb->setMaxResults($limit);
        $resultCacheParams['type'][] = 'limit%d';
        $resultCacheParams['value'][] = $limit;
    }

    $resultCacheId = vsprintf(
        'Property_findRecommendations_'.implode('_', $resultCacheParams['type']),
        $resultCacheParams['value']
    );

    $query = $qb->getQuery();

    $query->useQueryCache(true);
    $query->setQueryCacheLifetime(21600);
    $query->useResultCache(true, 21600, $resultCacheId);

    return $query->getResult();
}

因此,我必须在服务类中定义两种缓存ID组合 - Property_findRecommendations_%d_%sProperty_findRecommendations_%d_%s_limit%d,一种使用限制,另一种使用限制。

protected function getCacheIds()
{
    return array(
        'MyBundle\Entity\Property' => array(
                'id' => 'Property_findRecommendations_%d_%s',
                'vars' => array(
                    array('value' => 1), // recommend_flag
                    array('value' => date('Ymd')),
                ),
                'change' => 'any'
            ),
            array(
                'id' => 'Property_findRecommendations_%d_%s_limit%d',
                'vars' => array(
                    array('value' => 1), // recommend_flag
                    array('value' => date('Ymd')),
                    array('value' => $this->container->getParameter('max_recommend_properties'))
                ),
                'change' => 'any'
            ),
    );
}

这仅供一个参数作为示例。该方法将有多个参数。更多参数,服务类中缓存ID定义的更多组合。我认为这真的很贵。

最糟糕的情况是具有接受动态page参数的分页查询。应使用不同的缓存ID缓存每个页面的每个查询结果,例如Property_getAllProperties_%d

Property_getAllProperties_1 // for page 1
Property_getAllProperties_2 // for page 2
Property_getAllProperties_3 // for page 3
....

我无法知道服务类的方法getCacheIds()中的当前页码。 我应该计算所有属性的页数,并在方法getCacheIds()中动态定义每个页码的缓存ID数组吗?

我觉得这很奇怪,似乎是一种解决方法。另外,我有依赖于各种参数的搜索查询。我认为很难用getCacheIds()静态定义。

是否有一种更好,更优雅的方式来使doctrine memcache结果缓存失效?

更新

我能想到的一个可能的解决方案是创建一个新的数据库表cache_key并保存该表中的每个缓存ID。我有一个函数来根据查询参数生成缓存ID。它以custom_prefix + sha1(serialize($arrayOfParameters))的格式返回缓存ID,例如Property_findRecommendations_xxxxxxProperty_getAllProperties_xxxxxx

每当我调用两个方法findRecommendations()getAllProperties()时,就会将这些缓存ID(使用REPLACE)插入到表cache_key中,如下所示:

-----------------------------------------------------
| entity      | cache_id                            |
-----------------------------------------------------
| Property    | Property_findRecommendations_xxxxxx |
| Property    | Property_getAllProperties_xxxxxx    |
-----------------------------------------------------

然后,我将能够使用实体名称CacheValidatorProperty服务类中检索它们。这样做的一个缺点是,必须在每次查询运行时执行额外的INSERT/REPLACE查询,并在每个SELECT事件上执行额外的onFlush

尽管如此,我无法决定是否值得,因为根据生命周期设置,所有缓存每6小时都会失效。

0 个答案:

没有答案