【发布时间】:2015-12-11 16:49:13
【问题描述】:
我正在为 Symfony 应用程序编写一个功能,允许用户提交产品评级。我在每次评分后计算产品评分的平均值,这样我就不需要在每次需要平均评分时运行可能很昂贵的AVG() 查询。
这是一个计算平均评分并保存的简单函数:
public function calculateAndSaveAverageRating(Product $product)
{
// Run the SUM() query and return a float containing the average,
// or null if there are no ratings for the product.
$calculatedAverage = $this
->em
->getRepository('AppBundle:Product')
->findAverageRating($product);
// Lookup an existing ProductRatingAverage entity if it exists. This
// stores the average value of ratings for each product. Returns null
// if there is no existing entity.
$existingAverageEntity = $this
->em
->getRepository('AppBundle:ProductRatingAverage')
->findOneBy(array('product' => $product));
// Save the calculated average if we got a non-null value. Otherwise
// there are no ratings for this product, so delete the existing
// average entity if it exists.
if ($calculatedAverage) {
// If we have an existing average entity, update it. Otherwise
// create a new one and store the average.
if ($existingAverageEntity) {
$existingAverageEntity->setAverage($calculatedAverage);
} else {
$existingAverageEntity = new ProductRatingAverage();
$existingAverageEntity->setProduct($product);
$existingAverageEntity->setAverage($calculatedAverage);
$this->em->persist($existingAverageEntity);
}
} else {
if ($existingAverageEntity) {
$this->em->remove($existingAverageEntity);
}
}
$this->em->flush();
}
但是这里有一些并发问题。这是其中的两个:
- 如果两个用户同时提交没有先前评分(或非常接近)的产品评分,则此代码将尝试为同一产品创建两个平均评分实体,但只能有一个(数据库唯一约束)。
- 如果两个用户同时(或非常接近)为具有先前评分的产品提交评分,则此代码可能会从计算的平均值中排除其中一个评分。
我可以采取不同的方法:在运行 AVG() 查询的查询前面放置一个过期缓存,并让它每 1 小时或其他时间过期。但是,我遇到了同样的问题:如果两个访问者同时触发缓存刷新,就会出现同样的并发问题。
我应该如何设计这段代码以尽量减少并发问题?
【问题讨论】: