我做了很多研究,发现了很多关于所有相关主题的信息。但是,我不相信我现在明白了,如何正确地将所有这些信息放在一起。
此应用程序是用PHP编写的。
对于查询,我使用PDO。
MySQL数据库配置为InnoDB。
SELECT ... FROM tableA;
// PHP looks at what comes back and does some logic.
INSERT INTO tableA ...;
INSERT INTO tableB ...;
条件:
这对我来说看起来像一个非常简单的问题。然而,我无法弄清楚,如何正确地做到这一点。所以我的问题是:
这是我当前计划的大纲,大大简化了:
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;
}
我觉得有很多事情可以做得更好,但我不知道如何:/
我愿意接受PHP,MySQL,PDO和InnoDB框架条件下的任何建议。
谢谢!
修改1(2018-06-01)
我觉得我的问题/问题需要更多的澄清。
如果有两个表, t1 和 t2 。
t1 有多列非唯一值。
t2 的细节与此问题无关。
一步一步:
从 t1 中选择多个列和行。
在PHP中分析检索到的数据。根据此分析的结果将数据集放在一起。
将数据集的一部分插入 t1 ,将部分插入 t2 。
其他信息:
在步骤1和3之间不允许来自不同连接的INSERT。这非常重要,因为每次进入 t1 的INSERT必须完全了解当前状态的表。我将更详细地描述这一点。我现在暂时离开 t2 ,以便更容易理解。
想象一下这个事件序列(连接con1和con2):
因此两个连接都看到处于相同状态的 t1 。但是,他们选择不同的信息。 Con1 获取收集的信息,对其执行某些逻辑,然后将数据插入 t1 中的新行。 Con2 也是如此,但使用不同的信息。
问题在于:两个连接都根据未考虑插入到 t1 中的其他连接的计算来插入数据,因为当他们从< t1 读取时,该信息不存在EM> T1
Con2 可能已在符合 con1 的SELECT语句的WHERE条件的 t1 中插入一行。换句话说:如果先前 con2 插入了它的行, con1 可能创建了完全不同的数据以插入 t1 。这就是说:两个INSERT可能完全使每个其他插入无效。
这就是为什么我要确保一次只有一个连接可以处理 t1 中的数据。没有其他连接可以写入,但在当前连接完成之前也不允许其他连接读取。
我希望这会澄清一些事情......:/
我的想法是:
我需要将INSERT插入2个表原子。 - &GT;我将使用一个交易。像这样:
try {
$pdo->beginTransaction();
// INSERT INTO t1 ...
// INSERT INTO t2 ...
$pdo->commit();
}
catch (Exception $e) {
$pdo->rollBack();
throw $e;
}
我需要确保没有其他连接写入或读取 t1 。这是我决定需要LOCK TABLES的地方。
但我对代码的外观并不满意,这就是为什么我来这里问这个问题(同时又是冗长的)。
编辑2(2018-06-01)
此过程不会经常运行。因此,不需要高性能和高效率。当然,这也意味着这两个过程相互推断的机会相当微小。 Stil,我想确保一切都不会发生。
答案 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
。您也可以吃蛋糕并吃掉它。也就是说,看起来插件被阻挡了,但是你可以并行地获得它们的性能优势。魔法。