我应该如何编程此评级功能以最小化并发问题?

时间:2015-12-11 16:49:14

标签: php symfony doctrine

我正在为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();
}

但这里有一些并发问题。这是其中两个:

  1. 如果两个用户同时(或非常接近)提交没有先前评级的产品的评级,则此代码将尝试为同一产品创建两个平均评级实体,此时只能有一个(数据库唯一)约束)。
  2. 如果两个用户同时(或非常接近)提交具有先前评级的产品的评级,则此代码可能会从计算的平均值中排除其中一个评级。
  3. 我可以采取不同的方法:在运行AVG()查询的查询前放置一个过期缓存,并使其每1小时或过期一次到期。但是,我遇到了同样的问题:如果两个访问者同时触发缓存刷新,则存在相同的并发问题。

    我应该如何设计此代码以最大限度地减少并发问题?

1 个答案:

答案 0 :(得分:0)

并发解决方案通常是锁定机制。有时只需要一行锁定,有时则锁定整个表。第一个事务应用表锁,然后搜索或修改数据的其余事务将一直等到第一个事务明确执行解锁。我建议您阅读以下链接http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/transactions-and-concurrency.html#locking-support