我的目标是避免死锁,所以我将所有锁集中在同一个地方,按表名排序,然后按ID升序:
SELECT * FROM table1 WHERE ID = 1 FOR UPDATE
SELECT * FROM table1 WHERE ID = 2 FOR UPDATE
SELECT * FROM table1 WHERE ID = 3 FOR UPDATE
SELECT * FROM table1 WHERE ID = 4 FOR UPDATE
SELECT * FROM table2 WHERE ID = 1 FOR UPDATE
SELECT * FROM table2 WHERE ID = 2 FOR UPDATE
SELECT * FROM table2 WHERE ID = 3 FOR UPDATE
SELECT * FROM table2 WHERE ID = 4 FOR UPDATE
但我想知道我是否可以使用IN()(可能更快一点)来做同样的事情
SELECT * FROM table1 WHERE ID IN(1,2,3,4) FOR UPDATE
SELECT * FROM table2 WHERE ID IN(1,2,3,4) FOR UPDATE
是否会按照IN()操作数指定的确切顺序锁定行,否则将使用“自然表排序”来应用锁定?
ID是所有表中的主要auto_increment字段,我不“重用”旧的已删除ID(因此理论上自然顺序应该始终是升序的)
提前感谢!
添加了更新:
UPDATE table1 SET t1="hello1" WHERE ID = 1;
UPDATE table1 SET t1="hello2" WHERE ID = 2;
UPDATE table1 SET t1="hello3" WHERE ID = 3;
UPDATE table1 SET t1="hello4" WHERE ID = 4;
UPDATE table2 SET t2="hello1" WHERE ID = 1;
UPDATE table2 SET t2="hello2" WHERE ID = 2;
UPDATE table2 SET t2="hello3" WHERE ID = 3;
UPDATE table2 SET t2="hello4" WHERE ID = 4;
...
COMMIT;
答案 0 :(得分:1)
好的,所以这个问题并不是很清楚你想要什么...所以这可能不是你问题的答案..但我做了一些测试,以帮助你可视化数据和IN()如何工作..所以我希望至少有帮助。
SETUP:
CREATE TABLE table1
(`id` int, `username` varchar(10), `t1` varchar(55));
INSERT INTO table1
(`id`, `username`, `t1`)
VALUES
(4, 'John', 'Hi1'),
(3, 'Ram ', 'Hi2'),
(2, 'Jack', 'Hi3'),
(1, 'Jill', 'Hi4');
CREATE TABLE table2
(`id` int, `username` varchar(10), `t1` varchar(55));
INSERT INTO table2
(`id`, `username`, `t1`)
VALUES
(1, 'Joe', 'Hey1'),
(2, 'Fes', 'Hey2'),
(3, 'Ned', 'Hey3'),
(4, 'Abe', 'Hey4');
我使table1
具有向后ID ..又名 4,3,2,1 ,然后table2
具有常规递增ID。 1,2,3,4 ......
1. SELECT * FROM table1
2. SELECT * FROM table2
3. SELECT * FROM table1 WHERE id = 1 OR id = 2 OR id = 3 OR id = 4 same result as 1
4. SELECT * FROM table1 WHERE id IN(1, 2, 3, 4).. same result as 1.
5. SELECT * FROM table1 WHERE id IN(1, 4, 3, 2).. same result as 1.
IN()将表中每行的id与IN()语句中指定的进行比较.. 如果匹配,则返回行..所以它返回数据“自然表排序”..更多信息HERE
有一种方法可以使用IN()语句执行更新,而无需写出每个更新。
UPDATE table1 SET t1= CONCAT('hello', ID) WHERE ID IN(1,2,3,4);
UPDATE table2 SET t2= CONCAT('hello', ID) WHERE ID IN(1,2,3,4);
您在这里所做的就是将'hello'字符串与ID
结合起来,并将列设置为它,因为这就是您发布的内容。我希望这有助于理解数据的提取方式。
对于要更新的锁定表,您应该将它们锁定为WRITE和UNLOCK以防止出现深度锁定。见post
LOCK TABLES table1 WRITE, table2 WRITE;
UPDATE table1 SET t1= CONCAT('hello', ID) WHERE ID IN(1,2,3,4);
UPDATE table2 SET t1= CONCAT('hello', ID) WHERE ID IN(1,2,3,4);
UNLOCK TABLES;
答案 1 :(得分:1)
即使有点不清楚,但MySQL's documentation中列出了问题答案的某些部分:
expr IN(值,...)
如果expr等于IN列表中的任何值,则返回1,否则返回 返回0. 如果所有值都是常量,则根据它们进行评估 到expr的类型和排序。然后搜索该项目 使用二进制搜索。
以下是你应该得到的:如果列表中的所有值都是常量,则使用二进制搜索对它们进行比较。
所以最后,如果你已经对值进行了排序并不重要,因为MySQL会对它们进行排序,即使它们不是。然而,这不是你的问题。现在让我们回到你的问题。
首先,当您使用InnoDb并且它们一直在发生时(至少对我而言),MySQL中的死锁绝对是可能的。您选择用于防止死锁的策略是有效的(根据某种顺序获取锁定)。但不幸的是,我不认为它会在MySQL中起作用。你看,即使在你的查询中清楚地说明了你想锁定哪些记录,但事实是they are not the only records that will be locked:
锁定读取,UPDATE或DELETE通常设置记录锁定 在处理SQL时扫描的每个索引记录 声明。是否存在WHERE条件并不重要 将排除该行的语句。 InnoDB不记得了 确切的WHERE条件,但只知道扫描了哪个索引范围。 锁通常是下一把钥匙锁,也可以阻止插入 记录前的“差距”。但是,间隙锁定可以 显式禁用,导致不使用下一键锁定。
因此很难说实际锁定了哪些记录。现在考虑MySQL在索引中搜索列表中的第一个值。正如我刚才所说,当MySQL正在扫描索引时,可能还会锁定更多记录。而且由于扫描索引不是按顺序发生的(或者至少是我所相信的),记录会被锁定而不管它们的顺序如何。这意味着不会阻止死锁。
最后一部分是我对情况的理解,我实际上从未在任何地方读过这篇文章。但理论上听起来是对的。然而,我真的希望有人证明我是错的(只是这样我可以更信任MySQL)。
答案 2 :(得分:1)
行按读取顺序锁定,因此无法保证订单。即使您添加了ORDER BY
子句,行也会在读取时被锁定,而不是按顺序排列。 Here is another good question有一些很好的答案