【发布时间】:2018-05-31 07:36:44
【问题描述】:
我做了很多研究,发现了很多关于所有相关主题的信息。但是,我不确定我现在是否了解如何将所有这些信息正确地组合在一起。
这个应用程序是用 PHP 编写的。
对于查询,我使用 PDO。
MySQL 数据库配置为 InnoDB。
我需要什么
SELECT ... FROM tableA;
// PHP looks at what comes back and does some logic.
INSERT INTO tableA ...;
INSERT INTO tableB ...;
条件:
- INSERT 必须是原子的。如果其中一个失败,我想回滚。
- 不允许在 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;
}
我觉得有很多地方可以做得更好,但我不知道如何:/
为什么会这样?
- 如果 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 的细节与这个问题无关。
我想做什么:
一步一步:
从t1中选择多列和多行。
在 PHP 中分析检索到的数据。根据该分析的结果整理出一个数据集。
将部分数据集插入 t1,将部分数据插入 t2。
附加信息:
- 到 2 个表中的 INSERT 必须是原子的。这可以通过交易来实现。
-
在第 1 步和第 3 步之间不允许发生来自不同连接的 INSERT。这非常重要,因为每一次 INSERT 到 t1 都必须在完全了解表的当前状态的情况下发生.我最好更详细地描述这一点。我暂时将 t2 排除在外,以使事情更容易理解。
想象一下这一系列事件(连接 con1 和 con2):
- con1: SELECT ... FROM t1 WHERE xyz;
- con1:PHP 处理信息。
- con2: SELECT ... FROM t1 WHERE uvw;
- con2:PHP 处理信息。
- con1: 插入 t1 ...;
- con2: 插入到 t1 ...;
所以两个连接都看到 t1 处于相同的状态。但是,他们选择不同的信息。 Con1 获取收集到的信息,对其进行一些逻辑处理,然后将数据插入到 t1 中的新行中。 Con2 做同样的事情,但使用不同的信息。
问题是这样的:两个连接插入的数据基于没有考虑到任何其他连接插入到 t1 的计算,因为当他们从 读取时,这些信息不存在t1.
Con2 可能在 t1 中插入了一行,该行将满足 con1 的 SELECT 语句的 WHERE 条件。换句话说:如果 con2 之前插入了它的行,con1 可能已经创建了完全不同的数据来插入 t1。这就是说:这两个 INSERT 可能已经完全使彼此的插入无效。
这就是为什么我要确保一次只有一个连接可以处理 t1 中的数据。在当前连接完成之前,不允许其他连接写入,也不允许其他连接读取。
我希望这可以澄清一些事情......:/
想法:
我的想法是:
-
我需要将 2 个表中的 INSERT 设置为原子的。 --> 我将为此使用事务。像这样的:
try { $pdo->beginTransaction(); // INSERT INTO t1 ... // INSERT INTO t2 ... $pdo->commit(); } catch (Exception $e) { $pdo->rollBack(); throw $e; } 我需要确保没有其他连接写入或读取 t1。这就是我决定需要 LOCK TABLES 的地方。
- 假设我必须使用 LOCK TABLES,我遇到了 LOCK TABLES 不支持事务的问题。这就是为什么我决定采用此处提出的解决方案 (https://dev.mysql.com/doc/refman/8.0/en/lock-tables-and-transactions.html) 以及 stackoverflow 上的多个答案。
但我对代码的外观不满意,这就是我来这里问这个(同时相当冗长)问题的原因。
编辑 2 (2018-06-01)
此进程不会经常运行。因此,对高性能和效率没有太大的需求。当然,这也意味着其中两个过程相互推断的机会非常小。不过,我想确保不会发生任何事情。
【问题讨论】:
-
你不是说B和C吗? SELECT from A.. INSERT INTO A 有点毫无意义。
-
不,我的意思是 SELECT from A.. INSERT INTO A. 这些是步骤: (1.) SELECT from A. (2.) PHP 查看结果,执行一些逻辑,将一起插入数据。 (3.) INSERT INTO A. 为什么这毫无意义?我错过了什么?
-
也许我最初的问题不清楚。我重写了我的这部分问题,以减少歧义。
标签: php mysql pdo locking innodb