2012-05-28 38 views
0

我有一个执行mysql pdo查询的php脚本。在这个脚本中有几个读取和写入相同的表当php脚本执行顺序相同的脚本快速启动两次时mysql脚本查询

举例来说,假设有4个查询,一个读取,一个写入,另一个读取,另一个写入,每个读取需要10秒执行,每个写入需要0.1秒执行。

如果我在1/100秒内从cli nohup php execute_queries.php &执行这个脚本两次,查询的执行顺序是什么?

在来自第二个实例的查询开始运行之前,是否需要完成脚本第一个实例的所有查询,或者在表被锁定写入之前第一次从这两个实例读取开始和结束?

注意:假设我使用MyISAM和发送写入更新的记录(IE,整个表被写入时锁定。)

回答

1

既然你不使用事务,那么没有的不会等待一个脚本中的所有查询完成,因此查询可能会重叠。

有一个叫做并发编程的完整研究领域,教导了这一点。

在数据库中它是关于事务,隔离级别和数据锁定的。

典型(简单)的竞争条件:

$visits = $pdo->query('SELECT visits FROM articles WHERE id = 44')->fetch()[0]['visits']; 
/* 
* do some time-consuming thing here 
* 
*/ 
$visits++; 
$pdo->exec('UPDATE articles SET visits = '.$visits.' WHERE id = 44'); 

上面竞争条件,如果2 PHP进程在另一个之后从数据库中读取一毫秒的访问可以轻松地将酸,并假设初始访问值为6,两者都会将其增加到7,并且都会将7写回到数据库中,即使期望的效果是2次访问将值增加2(最终值访问应该是8)。

解决方案是使用原子操作(因为操作很简单,可以简化为单个原子操作)。

UPDATE articles SET visits = visits+1 WHERE id = 44; 

原子操作由数据库引擎保证不会被其他进程/线程中断。通常,数据库必须对传入的更新进行排队,以便它们不会相互影响。排队显然会减慢速度,因为每个进程必须等待所有进程前面的进程,直到它有机会执行。

在操作较少简单,我们需要一个以上的语句:

SELECT @visits := visits FROM articles WHERE ID = 44; 
SET @visits = @visits+1; 
UPDATE articles SET visits = @visits WHERE ID = 44; 

但同样,即使在数据库级别3个单独的原子声明并不能保证产生原子结果。它们可以与其他操作重叠。就像PHP示例一样。

为了解决这个问题,你必须做到以下几点:

START TRANSACTION 
    SELECT @visits := visits FROM articles WHERE ID = 44 FOR UPDATE; 
    SET @visits = @visits+1; 
    UPDATE articles SET visits = @visits WHERE ID = 44; 
COMMIT;