让我们说我们有一些表
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)同时执行请求:
我认为这是很常见的问题。我怎么解决它? (如果可能的话,与Doctrine一起使用)
解决方案1。表锁。
在所有查询之前执行LOCK TABLE
,并在完成后释放它。
LOCK TABLE
查询中预测它。答案 0 :(得分:1)
1启动交易和锁定表
2运行插入查询
3检查是否通过了10分钟限制
对步骤3和解锁表
的结果进行4次提交或回滚事务处理答案 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