限制在并发时间段添加一行

时间:2014-04-10 09:18:22

标签: php mysql symfony doctrine

让我们说我们有一些表

foo:
    message: STRING
    created: DATETIME

我们希望用户每10分钟只能添加一行。

$em = // \Doctrine\ORM\EntityManager
$repo = $em->getRepository('Foo');

$now = new \DateTime;
$tenMinutesAgo = $now->sub(new \DateInterval('PT10M'));

// See if there are any Foos in 10 minutes interval
// [SELECT]
$count = (int) $repo->createQueryBuilder('t')
    ->select('COUNT(t.id)')
    ->where('t.created > :ten_minutes_ago')
    ->setParameter('ten_minutes_ago', $tenMinutesAgo)
    ->getQuery()
    ->getSingleScalarResult();

// sleep(X) to simulate possible downtime

// [IF SELECT]
if (0 === $count) {
    $foo = new Foo;
    $foo->setMessage('bar')
        ->setCreated($now);

    $em->persist($foo);
    // [INSERT]
    $em->flush();

    echo "Added!";
} else {
    echo "Cannot add, wait around 10 min.";
}

现在,2个用户(Alice和Bob)同时执行请求:

  • Alice:Perfoming [SELECT] ... [IF SELECT] ok,count = 0
  • Bob:Perfoming [SELECT] ... [IF SELECT] ok,count = 0
  • Alice:[插入] ......
  • Bob:[插入] ......

我认为这是很常见的问题。我怎么解决它? (如果可能的话,与Doctrine一起使用)

解决方案1。表锁。

在所有查询之前执行LOCK TABLE,并在完成后释放它。

  1. 实际上,这个例子可能太简单了。一个用户在10分钟内无法插入超过1行的速度。对于表锁,所有用户都必须等待,而另一个用户添加他的行?
  2. 太可悲了,使用Doctrine表锁可能会非常棘手。 (DQL生成别名,我们必须在本地LOCK TABLE查询中预测它。

3 个答案:

答案 0 :(得分:1)

1启动交易和锁定表

2运行插入查询

3检查是否通过了10分钟限制

对步骤3和解锁表

的结果进行4次提交或回滚事务处理

https://dev.mysql.com/doc/refman/5.0/en/lock-tables.html

答案 1 :(得分:1)

UPDATE tbl SET last = NOW() WHERE last < NOW() - INTERVAL 10 MINUTE;
$mod_ct = get number of rows modified
if ($mod_ct ...) then "wait..."

更新是原子的。 $ mod_ct告诉你它是否成功。剩下的就是采取两种行动之一。 (如果你需要在其中一个分支中再做一些SQL,那么使用InnoDB并使用BEGIN ... COMMIT。)

答案 2 :(得分:0)

只要您已经在t中至少有一行开头。以下内容适用于单个sql语句(单个SQL语句=单个事务,不应该有相同的&#39;锁定&#39;您可能正在努力解决的问题

insert into t( created)  
 select  now() from foo
where not  exists(select 1 from t where created > now() - interval 10 minute )
limit 1