在mysql更新中跳过锁定的行以避免锁定超时

时间:2013-03-17 01:49:22

标签: mysql ruby-on-rails locking innodb

我的问题类似于: Ignoring locked row in a MySQL query 除了我已经实现了接近所接受答案中建议的逻辑。我的问题是如何最初设置进程ID。所有服务器都运行一个类似的查询(代码在rails上的ruby中,但产生的mysql查询是):

UPDATE (some_table) SET process_id=(some process_id) WHERE (some condition on row_1) AND process_id is null  ORDER BY (row_1) LIMIT 100

现在发生的是所有进程尝试更新相同的行,它们被锁定并且超时等待锁定。我希望服务器忽略被锁定的行(因为在释放锁之后,process_id不再是null,所以这里没有锁定点)。 我可以尝试随机化一批记录进行更新,但问题是我想按照上面的查询中的row_1优先处理更新。 所以我的问题是,在mysql中是否有办法检查记录是否被锁定并忽略它是否存在?

1 个答案:

答案 0 :(得分:1)

不,没有办法忽略已锁定的行。您最好的选择是确保在任何延长的时间段内没有任何行锁定。这将确保任何锁定冲突的持续时间都很短。这通常意味着通过将行锁定在事务中(使用FOR UPDATE)并更新行以将其标记为“已锁定”来对行进行“建议”锁定。

例如,首先您要查找候选行而不锁定任何内容:

SELECT id FROM t WHERE lock_expires IS NULL AND lock_holder IS NULL <some other conditions>;

现在非常快速地锁定所需的行

START TRANSACTION;
SELECT * FROM t WHERE id = <id> AND lock_expires IS NULL AND lock_holder IS NULL;
UPDATE t SET lock_expires = <some time>, lock_holder = <me> WHERE id = <id>;
COMMIT;

(技术说明:如果您计划锁定多行,请始终按特定顺序锁定。按主键升序是一个不错的选择。无序或按随机顺序锁定会使程序陷入死锁来自竞争过程。)

现在您可以根据需要(少于lock_expires)处理您的行而不阻止任何其他进程(在非锁定选择期间它们将与行不匹配,因此将总是忽略它)。处理完行后,您可UPDATEDELETE id,也不会阻止任何内容。