在InnoDB中使用表锁的MySQL / InnoDB事务

时间:2018-05-31 07:36:45

标签: php mysql pdo locking innodb

我做了很多研究,发现了很多关于所有相关主题的信息。但是,我不相信我现在明白了,如何正确地将所有这些信息放在一起。

此应用程序是用PHP编写的。

对于查询,我使用PDO。

MySQL数据库配置为InnoDB。

我需要什么

SELECT ... FROM tableA;

// PHP looks at what comes back and does some logic.
INSERT INTO tableA ...;
INSERT INTO tableB ...;

条件:

  • INSERT需要是原子的。如果其中一个失败,我想回滚。
  • 在SELECTA和INSERT之间不允许从/向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;
}     

我觉得有很多事情可以做得更好,但我不知道如何:/

为什么会这样?

  • 如果INSERT失败,我需要某种事务才能进行回滚。
  • 我需要锁定tableA以确保不会发生其他INSERT或UPDATE。
  • 交易和表锁不能很好地协同工作 (https://dev.mysql.com/doc/refman/8.0/en/lock-tables-and-transactions.html
  • 我希望在我的应用程序的其余部分中使用自动提交作为标准,这就是我将其设置回" 1"最后。
  • 我真的不确定这个:但是我在某个地方接了,在锁定一张桌子之后,我可以(从当前连接中)只查询到这个表,直到我解锁它(这对我来说没有意义) 。这就是为什么我也锁定了tableB,否则我就不需要了。

我对完全不同的方法持开放态度

我愿意接受PHP,MySQL,PDO和InnoDB框架条件下的任何建议。

谢谢!

修改1(2018-06-01)

我觉得我的问题/问题需要更多的澄清。

起点:

如果有两个表, t1 t2

t1 有多列非唯一值。

t2 的细节与此问题无关。

我想做什么:

一步一步:

  1. t1 中选择多个列和行。

  2. 在PHP中分析检索到的数据。根据此分析的结果将数据集放在一起。

  3. 将数据集的一部分插入 t1 ,将部分插入 t2

  4. 其他信息:

    • 2个表中的INSERT必须是原子的。这可以使用交易来实现。
    • 在步骤1和3之间不允许来自不同连接的INSERT。这非常重要,因为每次进入 t1 的INSERT必须完全了解当前状态的表。我将更详细地描述这一点。我现在暂时离开 t2 ,以便更容易理解。

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

      1. con1:SELECT ... FROM t1 WHERE xyz;
      2. con1:PHP处理信息。
      3. con2:SELECT ... FROM t1 WHERE uvw;
      4. con2:PHP处理信息。
      5. con1:INSERT INTO t1 ...;
      6. con2:INSERT INTO t1 ...;
      7. 因此两个连接都看到处于相同状态的 t1 。但是,他们选择不同的信息。 Con1 获取收​​集的信息,对其执行某些逻辑,然后将数据插入 t1 中的新行。 Con2 也是如此,但使用不同的信息。

        问题在于:两个连接都根据未考虑插入到 t1 中的其他连接的计算来插入数据,因为当他们从< t1 读取时,该信息不存在EM> T1

        Con2 可能已在符合 con1 的SELECT语句的WHERE条件的 t1 中插入一行。换句话说:如果先前 con2 插入了它的行, con1 可能创建了完全不同的数据以插入 t1 。这就是说:两个INSERT可能完全使每个其他插入无效。

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

        我希望这会澄清一些事情......:/

    思想:

    我的想法是:

    1. 我需要将INSERT插入2个表原子。 - &GT;我将使用一个交易。像这样:

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

    3. 假设我必须使用LOCK TABLES,我遇到了LOCK TABLES不能识别事务的问题。这就是为什么我决定采用这里提出的解决方案(https://dev.mysql.com/doc/refman/8.0/en/lock-tables-and-transactions.html)以及stackoverflow的多个答案。
    4. 但我对代码的外观并不满意,这就是为什么我来这里问这个问题(同时又是冗长的)。

      编辑2(2018-06-01)

      此过程不会经常运行。因此,不需要高性能和高效率。当然,这也意味着这两个过程相互推断的机会相当微小。 Stil,我想确保一切都不会发生。

1 个答案:

答案 0 :(得分:1)

案例1:

BEGIN;
INSERT ..
INSERT ..
COMMIT;

其他连接在提交之前才会看到插入的行。也就是说,BEGIN...COMMIT使两个插入&#34;原子&#34;。

如果有任何失败,你仍然需要try / catch来处理它。

不要在InnoDB表上使用LOCK TABLES

不要为autocommit而烦恼; BEGIN..COMMIT会覆盖它。

我的陈述适用于(可能)所有框架。 (除非有些人没有&#34;尝试&#34;和&#34;赶上&#34;。)

案例2 :在预期可能修改它时锁定一行:

BEGIN;
SELECT ... FROM t1 FOR UPDATE;
... work with the values SELECTed
UPDATE t1 ...;
COMMIT;

这使得其他人远离行SELECTed,直到COMMIT

之后

案例3 :有时IODKU在单个原子语句中做两件事很有用:

INSERT ...
    ON DUPLICATE KEY UPDATE ...

而不是

BEGIN;
SELECT ... FOR UPDATE;
if no row found
    INSERT ...;
else
    UPDATE ...;
COMMIT;

第4类:经典银行业务示例:

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;

如果系统在两个UPDATEs之间崩溃,则第一次更新将被撤消。这可以防止系统失去资金转移的轨道。

案例5:也许接近OP想要的东西。它主要是案例2和案例1的组合。

BEGIN;
SELECT ... FROM t1 FOR UPDATE;   -- see note below
... work with the values SELECTed
INSERT INTO t1 ...;
COMMIT;

案例5的注意事项:SELECT..FOR UPDATE必须包含您不希望其他连接看到的所有行。这会延迟另一个连接直到此连接COMMITs。 (是的,这感觉很像LOCK TABLES t1 WRITE。)

案例6:&#34;处理&#34;需要在BEGIN内部.COMMIT需要太长时间。 (例如:典型的在线购物车。)

这需要InnoDB交易之外的锁定机制。一种方法(对购物车有用)是在一些额外的表中使用一行,让每个人都检查它。另一种方式(在单个连接中更实用)是使用GET_LOCK('foo')及其朋友。

一般性讨论

以上所有示例仅锁定所涉及的行,而不是整个表。这使得操作的侵入性更小,并允许系统处理更多活动。

另外,请阅读MVCC。这是封面下使用的一般技术,让一个连接在某个时刻看到表的值,即使其他连接正在修改表

&#34;防止插入&#34; - 使用MVCC,如果你开始SELECT,就像获得你所看到的一切的快照一样。在完成INSERTs所在的交易之前,您不会看到SELECT。您也可以吃蛋糕并吃掉它。也就是说,看起来插件被阻挡了,但是你可以并行地获得它们的性能优势。魔法。