了解pdo mysql事务

时间:2016-05-31 23:16:49

标签: php mysql pdo transactions

PHP Documentation说:

  

如果您之前从未遇到过交易,则会提供4个专业   特性:原子性,一致性,隔离性和耐久性(ACID)。在   外行的条款,在交易中进行的任何工作,即使是   分阶段进行,保证应用于数据库   安全,并且没有其他连接的干扰,当它是   提交。

问题:

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

按照我的意思做好准备 INTERFERING

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

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

如果我有两个代码相似/相同的脚本并且它们在同一时间运行:

script1.php script2.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()函数都会强制它们单独发生?

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

3 个答案:

答案 0 :(得分:14)

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

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

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

答案 1 :(得分:9)

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

如果您需要确保所有DML语句都已正确执行或根本未执行,则需要进行交易。

意味着

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

虽然正如Shadow中的优秀回答中所指出的那样,可能在这里使用具有适当隔离级别的事务,但这会让人感到困惑。您需要的是 table locking 。 InnoDB引擎允许你lock particular rows而不是锁定整个表,因此应该是首选。

如果您希望薪水为1200,那么请使用表锁。

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

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

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

如果您的目标不同,请更好地明确表达。

但同样重要的是:您必须了解一般的交易与单独的脚本执行无关。关于竞争条件的主题,您不了解交易但是在表/行锁定中。这是一个非常常见的混淆,你最好直接学习它:

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

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

答案 2 :(得分:4)

唉,"没有干扰"需要程序员的帮助。它需要BEGINCOMMIT来定义交易的范围'。和...

你的榜样不足。第一个语句需要SELECT ... FOR UPDATE。这告诉事务处理,UPDATE提取的行可能会有SELECT。该警告对于防止干扰至关重要。现在时间表上写着:

  • script1.php BEGINs
  • script2.php BEGINs
  • script1.php选择数据(FOR UPDATE
  • script2.php选择数据被阻止,等待
  • script1.php更新数据
  • script1.php commit()发生
  • script2.php选择数据(并将获取新提交的值)
  • script2.php更新数据
  • script2.php commit()发生

(注意:这不是'死锁',只是'等待'。