Php 使用Doctrine和Azure SQL进行并发读取的数据库悲观锁定
我想做的事 我有一个具有多个工作节点的云应用程序,每个工作节点可以处理n个导出请求。导出请求被放入数据库中的表中。每个导出请求只能运行一次。如果工作节点当前运行的导出请求少于n个,则它会每0.1-1秒检查一次,以查看是否有可以开始处理的导出请求 等待运行的导出请求使用存储过程组织到数据库端的优先级队列中。下面的函数(Php 使用Doctrine和Azure SQL进行并发读取的数据库悲观锁定,php,doctrine-orm,azure-sql-database,Php,Doctrine Orm,Azure Sql Database,我想做的事 我有一个具有多个工作节点的云应用程序,每个工作节点可以处理n个导出请求。导出请求被放入数据库中的表中。每个导出请求只能运行一次。如果工作节点当前运行的导出请求少于n个,则它会每0.1-1秒检查一次,以查看是否有可以开始处理的导出请求 等待运行的导出请求使用存储过程组织到数据库端的优先级队列中。下面的函数(queuePop())是调用数据库的函数。它从优先级队列中获取最高结果,标记输出请求被使用(考虑到状态不再被设置为“新”),然后返回导出请求以在别处进行处理。 public func
queuePop()
)是调用数据库的函数。它从优先级队列中获取最高结果,标记输出请求被使用(考虑到状态不再被设置为“新”),然后返回导出请求以在别处进行处理。
public function queuePop() {
$entity_manager->getConnection()->beginTransaction();
// Get every new export request
try {
$statment = $entity_manager->getConnection()->prepare( 'EXEC root.findAllNewExportRequests' );
$statment->execute();
$results = $statment->fetchAll();
if ( count( $results ) > 0 ) {
$queue_item = array (
'export_request_id' => $results[0]['request_id']
);
} else {
$entity_manager->getConnection()->commit();
return NULL;
}
// Return the export request if it exists
$export_request = $entity_manager->find( 'Application\Model\ExportRequest', $queue_item['export_request_id'], \Doctrine\DBAL\LockMode::PESSIMISTIC_READ );
if ( !$export_request ) {
// Export request not found
throw new Exception( 'Export request not found.' );
}
// Update the export request
$export_request->setStatus( 'QUEUED' );
// Update the export request in the database
$entity_manager->persist( $export_request );
$entity_manager->flush();
$entity_manager->getConnection()->commit();
return $queue_item;
} catch ( \Exception $ex ) {
$entity_manager->getConnection()->rollback();
exit();
}
}
问题
queuePop函数工作得很好,它确保单个工作节点永远不会多次运行导出请求,但是当有2个或更多节点时,有可能有多个工作节点接收请求(考虑到它收到的请求数量,这种情况经常发生)。我尝试实现悲观锁定,如图()所示,但它不起作用。文档不是很好,我找不到任何正确使用悲观锁定的例子
我需要知道,当一个工作节点正在读取/更新导出请求时,其他节点无法读取/更新导出请求(它们必须等待)
我正在使用的内容
- nginx版本:nginx/1.4.6(Ubuntu)
- Ubuntu 14.04.1 LTS(GNU/Linux 3.13.0-32-generix x86_64)
- PHP 5.5.9-1ubuntu4.3(fpm fcgi)
- Zend Engine v2.5.0和Zend OPcache v7.0.3
- 原则2
- Azure SQL
虽然悲观_READ only会阻止行被更新,但不会被读取您不需要存储过程来完成此操作。在模型和请求队列表中添加一个足够长的
char
列,名为hash
,在处理下一个请求之前从工作进程中计算一个随机唯一ID(PHP内置函数uniqid()
是一个很好的选择),并发出以下SQL:
updateexport\u请求集status=“queued”,hash=:uniqueHash
WHERE status=“等待”
按id订购ASC限制1
(将参数uniqueHash
设置为之前计算的唯一ID,假设ID
可用于确定优先级)
接下来,通过作为“标记”传递到哈希列的uniqueHash
检索刚刚标记为“排队”的实体
诀窍在于更新查询是完全原子化的,您不会因为竞争条件而冒任何重复处理的风险。您甚至不需要为这两条语句设置一个周围事务(更新查询将在自动提交模式下的事务中运行,并且您的DBMS将确保不会有任何行排队两次,因为在哈希字段设置为您的唯一ID的同时,
status=“waiting”
条件将无效).谢谢你的解释,我从文件中不知道是哪个做的。我刚刚尝试过这个,但问题仍然存在。