MySQL : Can I use one SELECT . .. 更新到 "protect"多个表? (锁定)

标签 mysql select transactions locking innodb

我读了几个小时的 MySQL 文档,但我仍然无法回答自己几个非常简单的问题......:(

这是我的(简化的)场景:我的数据库中有两个表:tableatableb,这两个表都使用 InnoDB 存储引擎。 tablea(这是我的主表)有一个带有自动增量的主索引(id)。现在这就是我想要实现的目标,请记住以下业务逻辑可以并且将会同时运行:

我开始一笔交易: 开始交易 开始 然后我检查 tablea 中是否存在 id 如果是,我选择要更新的行,让我们调用我正在查找的 id myid : 从“tablea”中选择“id”,其中“id”=“myid”进行更新; 如果上面的 SELECT 没有返回任何行,我只需回滚事务并退出我的函数。换句话说,当 tablea 中不存在 myid 时,我就完成了。 另一方面,当 myid 存在时,我首先需要更新 tablea 中的一些值: UPDATE `tablea` SET `somefield`='somevalue' WHERE `id`='myid'; 然后我需要检查 myid 是否也存在于 tableb 中: SELECT * FROM `tableb` WHERE `id`='myid' 进行更新; 我的第一个问题是关于上面的 SELECT 语句:可以在此处(在 tableb 上)执行另一个 SELECT FOR UPDATE 吗???或者在处理tableb时,这里不需要“FOR UPDATE”,因为我已经启动了一个事务,并且还根据tablea中的一行获取了锁???有人可以回答一下吗?

上面的最后一个 SELECT 语句要么从 tableb 返回一行(并锁定该行以进行更新),要么结果发现 tableb 中不存在 myid。 当 myid 出现在 tableb 中时,我只需要更新该行中的一些值,这很简单: UPDATE `tableb` SET `somefieldintableb`='somevaluefortableb' WHERE `id`='myid'; 另一方面,当 myid 不在 tableb 中时,我需要插入它,这是我的第二个问题:我应该在发出 INSERT INTO 语句之前锁定 tableb 吗?这: 锁定表 `tableb` 写入; INSERT INTO `tableb` (`id`,`somefieldintableb`) VALUES ('myid','somevaluefortableb'); 解锁表`tableb`; 最后,我这样做: 犯罪

我的目标是这样的:由于上述函数(使用 MySQL 事务)将在许多实例中并行运行,我想防止任何这些实例更新 tableatableb 同时。我还想防止将 myid 重复插入到 tableb 中,因此当在 tableb 中找不到 myid 时,我考虑使用 LOCK TABLES。

所以我有两个问题:当我想更新 tableb 或使用 SELECT 锁定 tableb 时,我应该在已启动的事务中执行 SELECT ... FOR UPDATE 操作吗? .. FOR UPDATE 是不必要的,因为在这种情况下,持有 tablea 上的锁已经“保护”了 tableb 免受同时更新的影响???我的意思是,这要归功于我开始交易的方式。

第二个问题:当我需要在 tableb 中插入新行时,我应该锁定整个表以进行插入吗?或者在这种情况下这是完全没有必要的? (我是否需要 LOCK TABLES tableb ?)

如果专家能为我回答这两个问题,我将不胜感激,因为在线阅读各种文档和示例根本无法帮助我回答这些问题。 :(

最佳答案

我会这样做:

BEGIN;

SELECT a.`id` AS a_id, b.`id` AS b_id 
FROM `tablea` AS a LEFT OUTER JOIN `tableb` AS b ON a.id=b.id
WHERE a`id`='myid' 
FOR UPDATE;

现在,如果存在行,则 tablea 和 tableb 上都有行锁。如果 SELECT 没有返回任何内容,则表明该 id 不存在于 tablea 中。如果 SELECT 返回的行的 a_id 值为 NULL,但 b_id 为 NULL,那么您就知道它存在于 tablea 中而不是 tableb 中。

如果两个表中都存在该行,则会同时锁定两个表中的行。如果您分两步执行此操作,则可能会面临竞争条件和死锁的风险。

尝试插入并使用重复 key 更新:

INSERT INTO `tableb` (id, somefieldintableb) VALUES ('myid', 'somevaluefortableb') 
ON DUPLICATE KEY UPDATE `somefieldintableb`='somevaluefortableb';

如果具有所需 id 值的行不存在,则会将其插入。如果该行存在,这将更新该行。而且您肯定有权访问现有行,因为您的 SELECT FOR UPDATE 之前已锁定它。

如果可以避免的话,不要使用表锁。这肯定会在应用程序中造成瓶颈。


回复您的评论:

是的,您可以对日期列使用额外的连接条件。

当您使用 ON DUPLICATE KEY UPDATE 时,您不必更新所有列。如果该行存在,您可以保留其中的大部分,只更新一个或几个,或其他任何内容。

您还可以引用您尝试插入的值。

INSERT INTO `tableb` (id, date, col1, col2, col3, col4, col5, col6) 
  VALUES ('myid', $a_date, ?, ?, ?, ?, ?, ?) 
ON DUPLICATE KEY UPDATE col4=VALUES(col4);

有关更多详细信息,我建议阅读 http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html

关于MySQL : Can I use one SELECT . .. 更新到 "protect"多个表? (锁定),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38544770/

相关文章:

php - 在数据库中选择后没有返回

mysql - 如何为以下操作创建 SQL 查询?

javascript - Jquery-nice-select 插件无法正常工作

sql - 用 Select 查询替换 While 循环

oracle - "ORA-14450: attempt to access a transactional temp table already in use"在复合触发器中

magento - 什么是 Mage::getModel ('core/resource_transaction' )以及何时在 Magento 中使用此模型?

mysql - 如何通过mysql中带有某些前缀的行的自动增量值来更新行的特定字段(列)?

php - Num_Rows 中未选择任何数据库

mysql - 在 View 的结果集中返回具有非聚合列的聚合列的最佳方法是什么

mysql - MySQL 有多少实时未提交事务