了解 pdo mysql 事务

2022-08-30 23:19:53

PHP文档说:

如果您以前从未遇到过交易,它们提供了4个主要功能:原子性,一致性,隔离性和持久性(ACID)。通俗地说,在事务中执行的任何工作,即使它是分阶段执行的,也保证在提交时安全地应用于数据库,并且不受其他连接的干扰。

问题:

这是否意味着我可以让两个单独的php脚本同时运行事务而不会相互干扰?


详细阐述我的意思干扰

想象一下,我们有下表:employees

 __________________________
|  id  |  name  |  salary  |
|------+--------+----------|
|  1   |  ana   |   10000  |
|------+--------+----------|

如果我有两个具有相似/相同代码的脚本,并且它们完全相同地运行:

script1.phpscript2.php(两者具有相同的代码):

$conn->beginTransaction();

$stmt = $conn->prepare("SELECT * FROM employees WHERE name = ?");
$stmt->execute(['ana']);
$row = $stmt->fetch(PDO::FETCH_ASSOC);

$salary = $row['salary'];
$salary = $salary + 1000;//increasing salary

$stmt = $conn->prepare("UPDATE employees SET salary = {$salary} WHERE name = ?");
$stmt->execute(['ana']);

$conn->commit(); 

并假设事件的顺序如下:

  • 脚本 1.php 选择数据

  • 脚本 2.php 选择数据

  • 脚本 1.php更新数据

  • 脚本 2.php 更新数据

  • script1.php commit() 发生

  • script2.php commit() 发生

在这种情况下,ana的薪水会是多少?

  • 会是11000吗?这是否意味着 1 个事务将与其他事务重叠,因为信息是在任一提交发生之前获得的?

  • 会是12000年吗?那么,这是否意味着无论数据的更新和选择顺序如何,函数都会强制这些情况单独发生?commit()

请随时尽可能多地详细说明事务和单独的脚本如何相互干扰(或不干扰)。


答案 1

你不会在php文档中找到答案,因为这与php或pdo无关。

mysql中的Innodb表引擎提供了4个符合sql标准的所谓隔离级别。隔离级别与阻塞/非阻塞读取相结合将决定上述示例的结果。您需要了解各种隔离级别的含义,并根据您的需求选择合适的隔离级别。

总而言之:如果在关闭自动提交的情况下使用可序列化隔离级别,则结果将为 12000。在所有其他隔离级别和启用了自动提交的可序列化中,结果将为 11000。如果开始使用锁定读取,则在所有隔离级别下,结果可能是 12000。


答案 2

根据给定的条件(单独的DML语句)判断,您在这里不需要事务,而是需要表锁。这是一个非常普遍的困惑。

如果您需要确保所有 DML 语句都已正确执行或根本没有执行,则需要事务。

方法

  • 您不需要为任意数量的 SELECT 查询进行事务
  • 如果只执行一个 DML 语句,则不需要事务

尽管正如Shadow的优秀答案所指出的那样,您可以在此处使用具有适当隔离级别的事务,但这会相当令人困惑。这里您需要的是表锁定。InnoDB引擎允许您锁定特定的行,而不是锁定整个表,因此应该是首选。

如果您希望薪水为1200 - 请使用表锁。

或者 - 更简单的方法 - 只需运行原子更新查询:

UPDATE employees SET salary = salary + 1000 WHERE name = ?

在这种情况下,所有工资将被记录。

如果你的目标不同,最好明确地表达出来。

但同样:你必须明白,一般来说,事务与单独的脚本执行无关。关于争用条件的主题,您感兴趣的不是事务,而是表/行锁定。这是一个非常常见的困惑,你最好直接学习:

  • 事务是为了确保一个脚本中的一DML 查询已成功执行。
  • 表/行锁定是为了确保其他脚本执行不会干扰。

事务和锁定干扰的唯一主题是死锁,但同样 - 只有在事务使用锁定的情况下才这样做。


推荐