既然你不使用事务,那么没有的不会等待一个脚本中的所有查询完成,因此查询可能会重叠。
有一个叫做并发编程的完整研究领域,教导了这一点。
在数据库中它是关于事务,隔离级别和数据锁定的。
典型(简单)的竞争条件:
$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;