Php 使用Doctrine和Azure SQL进行并发读取的数据库悲观锁定

Php 使用Doctrine和Azure SQL进行并发读取的数据库悲观锁定,php,doctrine-orm,azure-sql-database,Php,Doctrine Orm,Azure Sql Database,我想做的事 我有一个具有多个工作节点的云应用程序,每个工作节点可以处理n个导出请求。导出请求被放入数据库中的表中。每个导出请求只能运行一次。如果工作节点当前运行的导出请求少于n个,则它会每0.1-1秒检查一次,以查看是否有可以开始处理的导出请求 等待运行的导出请求使用存储过程组织到数据库端的优先级队列中。下面的函数(queuePop())是调用数据库的函数。它从优先级队列中获取最高结果,标记输出请求被使用(考虑到状态不再被设置为“新”),然后返回导出请求以在别处进行处理。 public func

我想做的事

我有一个具有多个工作节点的云应用程序,每个工作节点可以处理n个导出请求。导出请求被放入数据库中的表中。每个导出请求只能运行一次。如果工作节点当前运行的导出请求少于n个,则它会每0.1-1秒检查一次,以查看是否有可以开始处理的导出请求

等待运行的导出请求使用存储过程组织到数据库端的优先级队列中。下面的函数(
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

您应该使用悲观写 (条令\DBAL\LockMode::悲观_-WRITE),为并发读写操作锁定底层数据库行。
虽然悲观_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”
条件将无效).

谢谢你的解释,我从文件中不知道是哪个做的。我刚刚尝试过这个,但问题仍然存在。