0

我有一个执行 mysql pdo 查询的 php 脚本。在此脚本中,对同一个表进行了几次读取和写入。

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

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

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

注意:假设我正在使用 myisam 并且写入是对记录的更新(IE,整个表在写入期间被锁定。)

4

1 回答 1

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;
于 2012-05-28T22:00:26.980 回答