MySQL :我可以使用一个 SELECT ... FOR UPDATE 到 "protect" 多个表吗? (锁定)
MySQL : Can I use one SELECT ... FOR UPDATE to "protect" multiple tables? ( LOCKING )
我正在阅读 MySQL 文档几个小时,但我仍然无法回答自己几个非常简单的问题...:(
这是我的(简化的)场景:我在数据库中有两个 table:tablea
和 tableb
,两个 table 都使用 InnoDB 存储引擎. tablea
(这是我的主要 table)有一个自动增量的主索引(id
)。现在这是我想要实现的目标,请记住以下业务逻辑可以并且将同时 运行:
我开始交易:
START TRANSACTION
BEGIN
然后我检查 tablea
中是否存在 id 如果是,我 SELECT 更新行,让我们调用我正在寻找的 id myid :
SELECT `id` FROM `tablea` WHERE `id`='myid' FOR UPDATE;
如果上面 SELECT returns 没有行,我只是回滚事务并退出我的函数。换句话说,当 tablea
中不存在 myid 时,我就完成了。
另一方面,当 myid 存在时,我首先需要更新 tablea
中的一些值:
UPDATE `tablea` SET `somefield`='somevalue' WHERE `id`='myid';
然后我需要检查 myid 是否也存在于 tableb
中:
SELECT * FROM `tableb` WHERE `id`='myid' FOR UPDATE;
我的第一个问题是关于上面的 SELECT 声明:可以在这里(在 tableb
上)做另一个 SELECT FOR UPDATE 吗???或者在处理tableb
时这里不需要"FOR UPDATE",因为我已经启动了一个事务并且还根据tablea
中的一行获取了锁???有人可以回答这个问题吗?
上面的最后一个 SELECT 语句 returns 来自 tableb
的一行(并锁定该行以进行更新)或者结果是 [=14= 中不存在 myid ].
当 myid 出现在 tableb
中时,我只需要更新该行中的一些值,这很简单:
UPDATE `tableb` SET `somefieldintableb`='somevaluefortableb' WHERE `id`='myid';
另一方面,当 myid 不在 tableb
中时,我需要插入它,这是我的第二个问题:我应该在发出 INSERT INTO 语句之前锁定 tableb
,如下所示:
LOCK TABLES `tableb` WRITE;
INSERT INTO `tableb` (`id`,`somefieldintableb`) VALUES ('myid','somevaluefortableb');
UNLOCK TABLES `tableb`;
最后,我这样做:
COMMIT
我的目标是:由于上述函数(使用 MySQL 事务)将 运行 在许多实例中并行,我想防止这些实例中的任何一个更新同一行同时 tablea
或 tableb
。我还想防止将 myid 重复插入 tableb
,因此我考虑在 tableb
.
中找不到 myid 时使用 LOCK TABLES
所以我有两个问题:当我想更新 tableb
或使用 [= 锁定 tableb
时,我是否应该在我已经开始的事务中执行 SELECT ... FOR UPDATE 68=] ... FOR UPDATE 是不必要的,因为在这种情况下从同步更新中也已经 "protects" tableb
锁定了 tablea
???感谢我开始交易的方式,我是说。
第二个问题:当我需要在 tableb
中插入一个新行时,我应该为该插入锁定整个 table 吗?还是在这种情况下完全没有必要? (我需要 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 if 行都存在行锁。如果 SELECT returns 什么都没有,您就知道该 id 不存在于 tablea 中。如果 SELECT returns 行的值为 a_id,但 b_id 的值为 NULL,那么您知道它存在于 tablea 而不是 tableb.
如果该行同时存在于两个 table 中,这将同时锁定两个 table 中的行。如果分两步执行,则可能会出现竞争条件和死锁。
尝试插入并使用 ON DUPLICATE KEY UPDATE:
INSERT INTO `tableb` (id, somefieldintableb) VALUES ('myid', 'somevaluefortableb')
ON DUPLICATE KEY UPDATE `somefieldintableb`='somevaluefortableb';
如果不存在具有所需 ID 值的行,则会将其插入。如果该行存在,这将更新该行。而且您肯定可以访问现有行,因为您的 SELECT FOR UPDATE 早些时候锁定了它。
如果可以避免,请不要使用 table 锁。这肯定会在您的应用程序中造成瓶颈。
回复您的评论:
是的,您可以对日期列使用额外的连接条件。
当您使用 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 文档几个小时,但我仍然无法回答自己几个非常简单的问题...:(
这是我的(简化的)场景:我在数据库中有两个 table:tablea
和 tableb
,两个 table 都使用 InnoDB 存储引擎. tablea
(这是我的主要 table)有一个自动增量的主索引(id
)。现在这是我想要实现的目标,请记住以下业务逻辑可以并且将同时 运行:
我开始交易:
START TRANSACTION
BEGIN
然后我检查 tablea
中是否存在 id 如果是,我 SELECT 更新行,让我们调用我正在寻找的 id myid :
SELECT `id` FROM `tablea` WHERE `id`='myid' FOR UPDATE;
如果上面 SELECT returns 没有行,我只是回滚事务并退出我的函数。换句话说,当 tablea
中不存在 myid 时,我就完成了。
另一方面,当 myid 存在时,我首先需要更新 tablea
中的一些值:
UPDATE `tablea` SET `somefield`='somevalue' WHERE `id`='myid';
然后我需要检查 myid 是否也存在于 tableb
中:
SELECT * FROM `tableb` WHERE `id`='myid' FOR UPDATE;
我的第一个问题是关于上面的 SELECT 声明:可以在这里(在 tableb
上)做另一个 SELECT FOR UPDATE 吗???或者在处理tableb
时这里不需要"FOR UPDATE",因为我已经启动了一个事务并且还根据tablea
中的一行获取了锁???有人可以回答这个问题吗?
上面的最后一个 SELECT 语句 returns 来自 tableb
的一行(并锁定该行以进行更新)或者结果是 [=14= 中不存在 myid ].
当 myid 出现在 tableb
中时,我只需要更新该行中的一些值,这很简单:
UPDATE `tableb` SET `somefieldintableb`='somevaluefortableb' WHERE `id`='myid';
另一方面,当 myid 不在 tableb
中时,我需要插入它,这是我的第二个问题:我应该在发出 INSERT INTO 语句之前锁定 tableb
,如下所示:
LOCK TABLES `tableb` WRITE;
INSERT INTO `tableb` (`id`,`somefieldintableb`) VALUES ('myid','somevaluefortableb');
UNLOCK TABLES `tableb`;
最后,我这样做:
COMMIT
我的目标是:由于上述函数(使用 MySQL 事务)将 运行 在许多实例中并行,我想防止这些实例中的任何一个更新同一行同时 tablea
或 tableb
。我还想防止将 myid 重复插入 tableb
,因此我考虑在 tableb
.
所以我有两个问题:当我想更新 tableb
或使用 [= 锁定 tableb
时,我是否应该在我已经开始的事务中执行 SELECT ... FOR UPDATE 68=] ... FOR UPDATE 是不必要的,因为在这种情况下从同步更新中也已经 "protects" tableb
锁定了 tablea
???感谢我开始交易的方式,我是说。
第二个问题:当我需要在 tableb
中插入一个新行时,我应该为该插入锁定整个 table 吗?还是在这种情况下完全没有必要? (我需要 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 if 行都存在行锁。如果 SELECT returns 什么都没有,您就知道该 id 不存在于 tablea 中。如果 SELECT returns 行的值为 a_id,但 b_id 的值为 NULL,那么您知道它存在于 tablea 而不是 tableb.
如果该行同时存在于两个 table 中,这将同时锁定两个 table 中的行。如果分两步执行,则可能会出现竞争条件和死锁。
尝试插入并使用 ON DUPLICATE KEY UPDATE:
INSERT INTO `tableb` (id, somefieldintableb) VALUES ('myid', 'somevaluefortableb')
ON DUPLICATE KEY UPDATE `somefieldintableb`='somevaluefortableb';
如果不存在具有所需 ID 值的行,则会将其插入。如果该行存在,这将更新该行。而且您肯定可以访问现有行,因为您的 SELECT FOR UPDATE 早些时候锁定了它。
如果可以避免,请不要使用 table 锁。这肯定会在您的应用程序中造成瓶颈。
回复您的评论:
是的,您可以对日期列使用额外的连接条件。
当您使用 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