繁体   English   中英

了解 pdo mysql 事务

[英]Understanding pdo mysql transactions

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(); 

并假设事件的顺序如下:

  • script1.php选择数据

  • script2.php选择数据

  • script1.php更新数据

  • script2.php更新数据

  • script1.php commit() 发生

  • script2.php commit() 发生

在这种情况下,ana 的工资是多少?

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

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

请随意详细说明事务和单独的脚本如何相互干扰(或不干扰)。

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

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

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

根据给定的条件(一个单独的 DML 语句)判断,这里不需要事务,而是需要表锁。 这是一个非常常见的混淆。

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

手段

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

虽然,正如 Shadow 的优秀回答中所指出的,您可以在此处使用具有适当隔离级别的事务,但这会相当混乱。 您在这里需要的是表锁定 InnoDB 引擎允许您锁定特定行而不是锁定整个表,因此应该是首选。

如果您希望工资为 1200 - 则使用表锁。

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

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

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

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

但同样:您必须了解事务一般与单独的脚本执行无关。 关于您的竞争条件主题,您不是对事务感兴趣,而是对表/行锁定感兴趣。 这是一个非常常见的混淆,你最好直接学习:

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

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

唉,“无干扰”需要程序员的一些帮助。 它需要BEGINCOMMIT来定义“事务”的范围。 还有...

你的例子是不够的。 第一条语句需要SELECT ... FOR UPDATE 这告诉事务处理对于SELECT获取的行可能会有一个UPDATE 该警告对于“防止干扰”至关重要。 现在时间线是这样的:

  • script1.php 开始
  • script2.php 开始
  • script1.php 选择数据( FOR UPDATE
  • script2.php选择数据被阻塞,所以等待
  • script1.php 更新数据
  • script1.php commit() 发生
  • script2.php 选择数据(并将获得新提交的值)
  • script2.php 更新数据
  • script2.php commit() 发生

(注意:这不是“死锁”,只是“等待”。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM