简体   繁体   English

了解 pdo mysql 事务

[英]Understanding pdo mysql transactions

The PHP Documentation says: PHP 文档说:

If you've never encountered transactions before, they offer 4 major features: Atomicity, Consistency, Isolation and Durability (ACID).如果您以前从未接触过事务,它们提供了 4 个主要特性:原子性、一致性、隔离性和持久性 (ACID)。 In layman's terms, any work carried out in a transaction, even if it is carried out in stages, is guaranteed to be applied to the database safely, and without interference from other connections, when it is committed.通俗地说,在事务中执行的任何工作,即使是分阶段执行的,在提交时都可以保证安全地应用于数据库,并且不受其他连接的干扰。

QUESTION:问题:

Does this mean that I can have two separate php scripts running transactions simultaneously without them interfering with one another?这是否意味着我可以让两个单独的 php 脚本同时运行事务而不会相互干扰?


ELABORATING ON WHAT I MEAN BY " INTERFERING " :详细说明我所说的干扰”是什么意思

Imagine we have the following employees table:假设我们有以下employees表:

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

If I have two scripts with similar/same code and they run at the exact same time:如果我有两个具有相似/相同代码的脚本并且它们同时运行:

script1.php and script2.php (both have the same code): 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(); 

and assuming the sequence of events is as follows:并假设事件的顺序如下:

  • script1.php selects data script1.php选择数据

  • script2.php selects data script2.php选择数据

  • script1.php updates data script1.php更新数据

  • script2.php updates data script2.php更新数据

  • script1.php commit() happens script1.php commit() 发生

  • script2.php commit() happens script2.php commit() 发生

What would the resulting salary of ana be in this case?在这种情况下,ana 的工资是多少?

  • Would it be 11000?会是11000吗? And would this then mean that 1 transaction will overlap the other because the information was obtained before either commit happened?这是否意味着 1 个事务将与另一个事务重叠,因为信息是在任一提交发生之前获得的?

  • Would it be 12000?会是12000吗? And would this then mean that regardless of the order in which data was updated and selected, the commit() function forced these to happen individually?那么这是否意味着无论更新和选择数据的顺序如何, commit()函数都会强制这些单独发生?

Please feel free to elaborate as much as you want on how transactions and separate scripts can interfere (or don't interfere) with one another.请随意详细说明事务和单独的脚本如何相互干扰(或不干扰)。

You are not going to find the answer in php documentation because this has nothing to do with php or pdo.您不会在 php 文档中找到答案,因为这与 php 或 pdo 无关。

Innodb table engine in mysql offers 4 so-called isolation levels in line with the sql standard. mysql中的innodb表引擎提供了4个所谓的符合sql标准的隔离级别 The isolation levels in conjunction with blocking / non-blocking reads will determine the result of the above example.隔离级别与阻塞/非阻塞读取结合将决定上述示例的结果。 You need to understand the implications of the various isolation levels and choose the appropriate one for your needs.您需要了解各种隔离级别的含义,并根据需要选择合适的级别。

To sum up: if you use serialisable isolation level with autocommit turned off, then the result will be 12000. In all other isolation levels and serialisable with autocommit enabled the result will be 11000. If you start using locking reads, then the result could be 12000 under all isolation levels.总结一下:如果你在关闭自动提交的情况下使用可序列化隔离级别,那么结果将是 12000。在所有其他隔离级别和启用自动提交的可序列化级别中,结果将是 11000。如果你开始使用锁定读取,那么结果可能是在所有隔离级别下为 12000。

Judging by the given conditions (a solitary DML statement), you don't need a transaction here, but a table lock.根据给定的条件(一个单独的 DML 语句)判断,这里不需要事务,而是需要表锁。 It's a very common confusion.这是一个非常常见的混淆。

You need a transaction if you need to make sure that ALL your DML statements were performed correctly or weren't performed at all.如果您需要确保所有 DML 语句都正确执行或根本没有执行,则需要一个事务

Means手段

  • you don't need a transaction for any number of SELECT queries您不需要任何数量的 SELECT 查询的事务
  • you don't need a transaction if only one DML statement is performed如果只执行一个 DML 语句,则不需要事务

Although, as it was noted in the excellent answer from Shadow, you may use a transaction here with appropriate isolation level, it would be rather confusing.虽然,正如 Shadow 的优秀回答中所指出的,您可以在此处使用具有适当隔离级别的事务,但这会相当混乱。 What you need here is table locking .您在这里需要的是表锁定 InnoDB engine lets you lock particular rows instead of locking the entire table and thus should be preferred. InnoDB 引擎允许您锁定特定行而不是锁定整个表,因此应该是首选。

In case you want the salary to be 1200 - then use table locks.如果您希望工资为 1200 - 则使用表锁。

Or - a simpler way - just run an atomic update query:或者 - 一种更简单的方法 - 只需运行原子更新查询:

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

In this case all salaries will be recorded.在这种情况下,所有工资都将被记录。

If your goal is different, better express it explicitly.如果你的目标不同,最好明确表达出来。

But again: you have to understand that transactions in general has nothing to do with separate scripts execution.但同样:您必须了解事务一般与单独的脚本执行无关。 Regarding your topic of race condition you are interested not in transactions but in table/row locking.关于您的竞争条件主题,您不是对事务感兴趣,而是对表/行锁定感兴趣。 This is a very common confusion, and you better learn it straight:这是一个非常常见的混淆,你最好直接学习:

  • a transaction is to ensure that a set of DML queries within one script were executed successfully.事务是为了确保一个脚本中的一组DML查询被成功执行。
  • table/row locking is to ensure that other script executions won't interfere.表/行锁定是为了确保其他脚本执行不会干扰。

The only topic where transactions and locking interfere is a deadlock , but again - it's only in case when a transaction is using locking.事务和锁定干扰的唯一主题是死锁,但同样 - 只有在事务使用锁定的情况下。

Alas, the "without interference" needs some help from the programmer.唉,“无干扰”需要程序员的一些帮助。 It needs BEGIN and COMMIT to define the extent of the 'transaction'.它需要BEGINCOMMIT来定义“事务”的范围。 And...还有...

Your example is inadequate.你的例子是不够的。 The first statement needs SELECT ... FOR UPDATE .第一条语句需要SELECT ... FOR UPDATE This tells the transaction processing that there is likely to be an UPDATE coming for the row(s) that the SELECT fetches.这告诉事务处理对于SELECT获取的行可能会有一个UPDATE That warning is critical to "preventing interference".该警告对于“防止干扰”至关重要。 Now the timeline reads:现在时间线是这样的:

  • script1.php BEGINs script1.php 开始
  • script2.php BEGINs script2.php 开始
  • script1.php selects data ( FOR UPDATE ) script1.php 选择数据( FOR UPDATE
  • script2.php selects data is blocked, so it waits script2.php选择数据被阻塞,所以等待
  • script1.php updates data script1.php 更新数据
  • script1.php commit() happens script1.php commit() 发生
  • script2.php selects data (and will get the newly-committed value) script2.php 选择数据(并将获得新提交的值)
  • script2.php updates data script2.php 更新数据
  • script2.php commit() happens script2.php commit() 发生

(Note: This is not a 'deadlock', just a 'wait'.) (注意:这不是“死锁”,只是“等待”。)

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

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