我在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_%s
和Property_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_xxxxxx
,Property_getAllProperties_xxxxxx
。
每当我调用两个方法findRecommendations()
和getAllProperties()
时,就会将这些缓存ID(使用REPLACE
)插入到表cache_key
中,如下所示:
-----------------------------------------------------
| entity | cache_id |
-----------------------------------------------------
| Property | Property_findRecommendations_xxxxxx |
| Property | Property_getAllProperties_xxxxxx |
-----------------------------------------------------
然后,我将能够使用实体名称CacheValidator
在Property
服务类中检索它们。这样做的一个缺点是,必须在每次查询运行时执行额外的INSERT/REPLACE
查询,并在每个SELECT
事件上执行额外的onFlush
。
尽管如此,我无法决定是否值得,因为根据生命周期设置,所有缓存每6小时都会失效。