2016-07-23 54 views
1

我正在阅读MySQL文档几个小时,但我仍然无法回答自己几个很简单的问题...... :(MySQL:我可以使用一个SELECT ... FOR UPDATE来“保护”多个表吗? (LOCKING)

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

我开始交易: START TRANSACTION BEGIN 然后我检查一个id是否存在在tablea如果是的话,我选择行FOR UPDATE,让我们打电话给我找我的ID: SELECT `id` FROM `tablea` WHERE `id`='myid' FOR UPDATE; 如果上面的SELECT返回没有行,我只需ROLLBACK事务并退出我的功能。换句话说,当我在tablea中不存在myid时,我就完成了。 就当身份识别码先存在,那么我需要tablea更新一些价值观另一方面: UPDATE `tablea` SET `somefield`='somevalue' WHERE `id`='myid'; 然后我需要检查,如果身份识别码也存在于tableb SELECT * FROM `tableb` WHERE `id`='myid' FOR UPDATE; 我的第一个问题是关于上面的SELECT语句:这是好的在这里做另一个SELECT FOR UPDATE(在tableb)???或“更新”是不是这里tableb打交道时,因为我已经开始交易需要,也收购了总部设在tablea一排锁???有人可以回答吗?

上面的最后一条SELECT语句从tableb返回一行(并锁定该行以进行更新),或者结果显示myid在tableb中不存在。 当身份识别码出现在tableb那么我只需要在该行中更新一些价值观,这很简单: UPDATE `tableb` SET `somefieldintableb`='somevaluefortableb' WHERE `id`='myid'; 在另一方面,当身份识别码不tableb我需要插入它,这才是我的第二个问题:我应该锁定tableb之前,我发出了我的INSERT INTO语句,就像这样: LOCK TABLES `tableb` WRITE; INSERT INTO `tableb` (`id`,`somefieldintableb`) VALUES ('myid','somevaluefortableb'); UNLOCK TABLES `tableb`; ,最后,我做的: COMMIT

我的目标是这样的:由于上述的功能(与MySQL的交易)会在许多情况下运行同时,我想阻止任何这些实例更新tableatableb中的同一行 与此同时。我也想防止身份识别码的双插入tableb,所以我想过使用LOCK TABLES时tableb没有被发现身份识别码。

所以我有两个问题:我应该做一个SELECT ... FOR我已经开始了事务中更新时,我想更新tableb或SELECT锁定tableb ... FOR UPDATE是不必要的,因为持有tablea锁在这种情况下已经同时更新“保护”tableb ???我的意思是,感谢我开始交易的方式。

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

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

回答

1

我会做这种方式:

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和表B行锁如果存在行如果SELECT没有返回,你知道ID不存在的如果SELECT返回值为a_id的行,但为b_id为NULL,那么您知道它存在于tablea中,而不是在tableb中。

如果该行在两个表中都存在,则会同时锁定行如果分两步执行,则可能会导致竞争状态和死锁。

尝试INSERT和使用对重复密钥更新:

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

+1

谢谢你,比尔!我有两个步骤做事的原因(我提到这只是一个简单的例子)。 tableb中的myid不是唯一的,而是在tableb中,这两个字段是唯一的(这是tableb中的PRIMARY索引,没有自动增量):'id','date'。所以在tableb中我每天只有一个(或零)ID。在这种情况下,我仍然可以使用单个SELECT FOR ... UPDATE吗?考虑一下,我可以做到这一点,但我需要为外部联接添加一个WHERE子句:AND tableb.date = DATE(NOW())您怎么看? – Zoltan

+0

我也不想使用ON DUPLICATE KEY UPDATE,因为:当myid存在于tableb中(对于今天的日期),那么我只需要更新tableb中该行的单个字段。但是,当myid不在tableb中(今天的日期),那么我需要插入总共6个字段值,并为额外的5个字段收集这些值是我的程序中的一个昂贵的操作。所以在可能的情况下,我只想更新tableb中的一个字段,而不是全部6个字段。 (简单的例子不工作!)) – Zoltan

+0

这工作?作为一个左外部连接'tableb'作为一个b.id = b.id和b.date = DATE(NOW())作为一个左外部连接'tableb'作为a_id, ) WHERE a'id' ='myid' ? – Zoltan

相关问题