Php InnoDB中带有表锁的MySQL/InnoDB事务

Php InnoDB中带有表锁的MySQL/InnoDB事务,php,mysql,pdo,locking,innodb,Php,Mysql,Pdo,Locking,Innodb,我做了很多研究,发现了很多关于所有相关主题的信息。然而,我不确定我现在是否理解如何正确地将所有这些信息组合在一起 这个应用程序是用PHP编写的 对于查询,我使用PDO MySQL数据库配置为InnoDB 我需要什么 条件: 插入必须是原子的。如果其中一个失败了,我想退出 在SELECT和INSERT from/into tableA之间不允许发生从/到tableA的读取和写入 在我看来,这是一个非常简单的问题。但我不知道如何正确地做到这一点。所以我的问题是: 最好的方法是什么? 这是我当前计

我做了很多研究,发现了很多关于所有相关主题的信息。然而,我不确定我现在是否理解如何正确地将所有这些信息组合在一起

这个应用程序是用PHP编写的

对于查询,我使用PDO

MySQL数据库配置为InnoDB

我需要什么 条件:

  • 插入必须是原子的。如果其中一个失败了,我想退出
  • 在SELECT和INSERT from/into tableA之间不允许发生从/到tableA的读取和写入
在我看来,这是一个非常简单的问题。但我不知道如何正确地做到这一点。所以我的问题是:

最好的方法是什么? 这是我当前计划的大纲,经过了大量简化:

try {
  SET autocommit = 0;

  BLOCK TABLES tableA WRITE, tableB WRITE;

  SELECT ... FROM tableA;

  INSERT INTO tableA ...;
  INSERT INTO tableB ...;

  COMMIT;

  UNLOCK TABLES;

  SET autocommit = 1;
}

catch {
  ROLLBACK;

  UNLOCK TABLES;

  SET autocommit = 1;
}     
我觉得有很多事情可以做得更好,但我不知道如何做:/

为什么会这样?
  • 如果插入失败,我需要某种事务来执行回滚
  • 我需要锁定tableA以确保没有其他插入或更新发生
  • 事务和表锁不能很好地协同工作 ()
  • 我希望在我的应用程序的其余部分使用autocommit作为标准,这就是为什么我在最后将其设置回“1”
  • 我真的不确定这一点:但我在某处发现,在锁定一个表之后,我只能(从当前连接中)查询这个表,直到我解锁它(这对我来说没有意义)。这就是为什么我也锁了tableB,尽管我不需要锁
我对完全不同的方法持开放态度 我愿意在PHP、MySQL、PDO和InnoDB框架条件下接受任何建议

谢谢


编辑1(2018-06-01)

我觉得我的问题需要进一步澄清

起点: 如果有两个表,t1和t2

t1有多列非唯一值

t2的细节与此问题无关

我想做的是: 逐步:

  • 从t1中选择多个列和行

  • 在PHP中,分析检索到的数据。根据这一分析的结果,整理出一个数据集

  • 将数据集的一部分插入t1,另一部分插入t2

  • 其他信息:

    • 2个表中的插入必须是原子的。这可以通过使用事务来实现
    • 在步骤1和步骤3之间,不允许出现来自不同连接的插入。这一点非常重要,因为对t1的每一次插入都必须完全了解表的当前状态。我最好更详细地描述一下。为了让事情更容易理解,我暂时不考虑t2

      想象一下这一系列事件(连接con1和con2):

    • con1:选择。。。从t1开始,其中xyz
    • con1:PHP处理这些信息
    • con2:选择。。。从t1开始,其中uvw
    • con2:PHP处理信息
    • con1:插入t1
    • con2:插入t1 因此,两个连接都可以看到处于相同状态的t1。但是,它们选择不同的信息。Con1获取收集到的信息,对其进行一些逻辑处理,然后将数据插入t1中的新行。Con2也是这样,但使用的信息不同

      问题在于:这两个连接都是基于计算插入数据的,而这些计算没有考虑其他连接插入t1的内容,因为当它们从t1读取数据时,这些信息并不存在

      Con2可能在t1中插入了一行,该行符合con1的SELECT语句的WHERE条件。换句话说:如果con2更早地插入了它的行,con1可能会创建完全不同的数据来插入t1。这就是说:这两个插入可能会使彼此的插入完全无效

      这就是为什么我想确保一次只有一个连接可以处理t1中的数据。在当前连接完成之前,不允许其他连接写入,也不允许其他连接读取

      我希望这能澄清一些事情…:/

    思想: 我的想法是:

  • 我需要在两个表中插入原子。-->我将为此使用事务处理。大概是这样的:

    try {
      $pdo->beginTransaction();
      // INSERT INTO t1 ...
      // INSERT INTO t2 ...
      $pdo->commit();
    }
    catch (Exception $e) {
      $pdo->rollBack();
      throw $e;
    }
    
  • 我需要确保没有其他连接写入或读取t1。这就是我决定我需要锁表的地方

  • 假设我必须使用锁表,我会遇到锁表不支持事务的问题。这就是为什么我决定采用这里提出的解决方案()以及关于stackoverflow的多个答案
  • 但我对代码的外观不满意,这就是为什么我来这里问这个(同时相当长)问题的原因


    编辑2(2018-06-01)


    这个过程不会经常运行。因此,对高性能和高效率没有太大的需求。当然,这也意味着其中两个过程相互推断的机会非常小。尽管如此,我还是想确保不会发生任何事情。

    案例1:

    BEGIN;
    INSERT ..
    INSERT ..
    COMMIT;
    
    在提交之后,其他连接才会看到插入的行。也就是说,
    BEGIN…COMMIT
    使两个插入“原子化”

    如果任何事情都失败了,您仍然需要尝试/捕获来处理它

    不要在InnoDB表上使用
    锁表

    不要费心于
    自动提交
    <代码>开始..提交覆盖它

    我的陈述(可能)适用于所有框架。(除了一些没有“尝试”和“捕获”。)

    案例2:锁定一行,以防可能修改它:

    BEGIN;
    SELECT ... FROM t1 FOR UPDATE;
    ... work with the values SELECTed
    UPDATE t1 ...;
    COMMIT;
    
    这会使其他人在
    提交
    之前远离选定的行

    案例3:有时IODKU对做两件事很有用
    BEGIN;
    SELECT ... FROM t1 FOR UPDATE;
    ... work with the values SELECTed
    UPDATE t1 ...;
    COMMIT;
    
    INSERT ...
        ON DUPLICATE KEY UPDATE ...
    
    BEGIN;
    SELECT ... FOR UPDATE;
    if no row found
        INSERT ...;
    else
        UPDATE ...;
    COMMIT;
    
    BEGIN;
    UPDATE accounts SET balance = balance - 1000.00 WHERE id='me';
    ... What if crash occurs here? ...
    UPDATE accounts SET balance = balance + 1000.00 WHERE id='you';
    COMMIT;
    
    BEGIN;
    SELECT ... FROM t1 FOR UPDATE;   -- see note below
    ... work with the values SELECTed
    INSERT INTO t1 ...;
    COMMIT;