我读了几个小时的MySQL文档,但我仍然不能回答自己几个非常简单的问题.:(
下面是我的(简化)场景:数据库中有两个表:tablea
和tableb
,两个表都使用InnoDB存储引擎。tablea
(它是我的主表)有一个带有自动增量的主索引(id
)。下面是我想要实现的目标,请记住,以下业务逻辑可以并将同时运行:
我启动了一个事务:START TRANSACTION BEGIN
,然后我检查tablea
中是否存在id,如果是,我选择要更新的行,让我们调用我要查找的id:SELECT `id` FROM `tablea` WHERE `id`='myid' FOR UPDATE;
,如果上面的SELECT没有返回任何行,我只是回滚事务并退出我的函数。换句话说,当我的I不在tablea
中时,我就完成了。另一方面,当myid存在时,首先我需要更新tablea
:UPDATE `tablea` SET `somefield`='somevalue' WHERE `id`='myid';
中的一些值,然后我需要检查myid是否也存在于tableb
:SELECT * FROM `tableb` WHERE `id`='myid' FOR UPDATE;
中,我的第一个问题是关于上面的SELECT语句:在这里为更新做另一个选择可以吗(在tableb
上)?或者在这里处理tableb
时不需要"FOR UPDATE“,因为我已经启动了一个事务,并且还根据tablea
??中的一个行获得了一个锁。有人能回答这个问题吗?
上面的最后一个SELECT语句要么从tableb
返回一行(并锁定该行以进行更新),要么证明tableb
中不存在myid。当myid出现在tableb
中时,我只需要更新该行中的一些值,这很简单:另一方面,当UPDATE `tableb` SET `somefieldintableb`='somevaluefortableb' WHERE `id`='myid';
不在tableb
时,我需要插入它,下面是我的第二个问题:在发出insert INSERT语句之前,是否应该锁定tableb
,比如:LOCK TABLES `tableb` WRITE; INSERT INTO `tableb` (`id`,`somefieldintableb`) VALUES ('myid','somevaluefortableb'); UNLOCK TABLES `tableb`;
,最后,我需要:COMMIT
我的目标是:由于上面描述的函数(与MySQL事务一起)将在许多实例中并行运行,所以我希望防止任何这些实例同时更新tablea
或tableb
中的同一行。我还想防止在tableb
中双重插入myid,因此我考虑在tableb
中找不到myid时使用锁表。
所以我有两个问题:我应该做一个选择.对于我已经启动的事务中的更新,当我想更新tableb
或使用SELECT锁定tableb
.因为没有必要进行更新,因为保持tablea
上的锁也可以“保护”tableb
免受同步更新的影响--在这种情况下?我是说,多亏了我开始交易的方式。
第二个问题:当我需要在tableb
中插入一个新行时,是否应该锁定整个表以进行插入?还是在这种情况下完全没有必要?(我是否需要锁表tableb
?)
如果专家能帮我回答这两个问题,我将不胜感激,因为在线阅读各种文档和例子对我回答这些问题毫无帮助。:(
发布于 2016-07-23 10:54:24
我会这样做:
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;
现在,如果存在行,则表A和tableb上都有行锁。如果SELECT不返回任何内容,则您知道该id不在表A中。如果SELECT返回值为a_id的行,但返回b_id的NULL,则您知道它存在于表A中,而不是在表表中。
如果两个表中都存在行,则这将同时锁定两个表中的行。如果分两步执行,则可能会面临竞争条件和死锁的风险。
尝试插入并使用重复的键更新:
INSERT INTO `tableb` (id, somefieldintableb) VALUES ('myid', 'somevaluefortableb')
ON DUPLICATE KEY UPDATE `somefieldintableb`='somevaluefortableb';
如果不存在具有所需id值的行,则将插入它。如果存在行,则将更新该行。而且您肯定可以访问现有的行,因为您选择的UPDATE已经锁定了它。
如果你能避免的话,不要使用表锁。这是在应用程序中创建瓶颈的可靠方法。
关于你的评论:
是的,您可以对date列使用额外的联接条件。
在使用重复键更新时,不必更新所有列。如果存在行,则可以将它们中的大多数单独处理,只需更新一个或几个行或其他任何内容。
此外,还可以引用尝试插入的值。
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
https://stackoverflow.com/questions/38544770
复制相似问题