我需要使用doctrine锁定整个表(不是单行),如果可能的话,我希望在没有本机查询的情况下执行此操作。
pessimistic locking的文档仅介绍了如何通过以下方法锁定特定实体:
我有一个事务需要插入一行,其值取决于表中其余行的值,因此我需要阻止在该表上同时执行两个事务。
我正在使用显式事务划分,这应该适用于锁定(根据上面的文档)。
注意:在这种情况下,乐观锁定不够好,我负担不起重试交易。此外,查询不应该很慢,因此性能不是问题。
编辑:我举个例子。想象一下,你想手工构建一个auto_increment,你必须从表中选择max()来获取前一个结果才能插入下一个结果。您必须确保没有两个事务尝试插入相同的值,以防他们同时选择max()。
当auto_increment不好时,我正在寻找这个问题的一般解决方案,例如使用字符串,或多列,哈希或您必须在前一行集上进行的任何计算。
锁定是一个可靠的解决方案,与乐观锁定不同,您不必重试错误。
那么,有没有办法在学说中使用表锁定?
答案 0 :(得分:2)
可能由Doctrine2 ORM select for update
重复以下是一些相关代码:
try {
$entity = $em->find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion);
// do the work
$em->flush();
} catch(OptimisticLockException $e) {
echo "Sorry, but someone else has already changed this entity. Please apply the changes again!";
}
LockMode :: OPTIMISTIC参数可能提供您所需的功能。
答案 1 :(得分:2)
到目前为止,根据建议,我尝试了这个:
$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access
// calculate $new_number...
// persist $new_number on table_name...
$table_name->setCalculatedNumber($new_number);
$em->persist($table_name);
$em->flush();
$em->getConnection()->exec('UNLOCK TABLES;');
我用JMeter测试了它,并且锁定不能用于重负载(16个请求/秒)。跟踪显示其他实例在明确放弃之前获得了锁定。问题(由Jens建议)是flush()隐式地以START TRANSACTION开头,它会丢弃表锁。 使用本机更新为我解决了问题:
$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access
// calculate $new_number...
// persist $new_number on table_name...
$em->getConnection()->executeUpdate("UPDATE table_name set ...;");
$em->getConnection()->exec('UNLOCK TABLES;');
$em->refresh($table_name);
需要尾随刷新()以使后续查询结果中的计算数字可用
答案 2 :(得分:1)
通过查看Doctrine 2.x中的文档,我认为没有一种支持的锁定整个表的方法。你当然可以尝试通过Doctrine单独锁定所有记录,但这很麻烦,并不是一个好主意。
相反,我会使用Doctrine实体管理器在数据库上执行原始SQL ...
$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access
然后在完成更新后......
$em->getConnection()->exec('UNLOCK TABLES;');
编辑:
从表格锁MySQL documentation开始......
持有锁的会话可以读写表。
只有持有锁的会话才能访问该表。在解除锁定之前,没有其他会话可以访问它。
在保持WRITE锁定的情况下,锁定其他会话阻止对该表的请求。
我认为这里的第二点是关键,只有你的会话才能读/写该表。