如何在不阻塞的情况下锁定mysql中的行?

时间:2014-09-08 05:57:33

标签: mysql sql transactions locking deadlock

我在mariadb中有一个表格,我保存了一些RSS源的链接:

id | url                    | done
------------------------------------
 1 | http://example.com/rss | true
 2 | http://example.org/rss | false
 3 | http://google.com/rss  | false

现在,当一个进程/工作者正在更新其中一个RSS提要时,我希望它隐藏来自其他进程/ worker的行,这样他们就不会做两次工作并且死锁。

使用SELECT ... IN SHARE MODESELECT ... FOR UPDATE不起作用,因为该行仍然可见,然后会被所有其他工作人员锁定。

所以下一个想法是使用一个事务,在另一个locked列上设置一个标志:

BEGIN;
    // find row and select its id
    SET @id := (SELECT id
    FROM url
    WHERE done = false
        AND locked = false
    LIMIT 1);

    // lock row by setting locked flag to true
    UPDATE url
    SET locked = true
    WHERE id = @id;

    // give us some time to copy and paste this sql in another console :)
    SELECT SLEEP(10);

    // unlock row and mark as done
    UPDATE url
    SET
        locked = false,
        done = true
    WHERE id = @id;
COMMIT;

以下SQL是针对sqlGoogle Go包执行的。

按预期工作,但它通过阻止所有其他事务来工作。这意味着当我同时执行上述sql事务到两个sql控制台时,我可以看到后面的事务如何等待第一个完成。 这种效果破坏了拥有多个工人的完整想法,因为他们最终会被交易同步。

如何在不阻止的情况下从其他交易“锁定”一行?

1 个答案:

答案 0 :(得分:1)

您正在使用LOCK IN SHARE MODE锁定来自UPDATEDELETE操作的行,但允许读取该行。有关详细信息,请参阅this documentation。文档的简要说明如下。

  

SELECT ... LOCK IN SHARE MODE在任何读取的行上设置共享模式锁定。其他会话可以读取行,但在事务提交之前无法修改它们。

     

对于搜索遇到的索引记录,SELECT ... FOR UPDATE阻止其他会话执行SELECT ... LOCK IN SHARE MODE或从某些事务隔离级别读取。

您的查询将同一行返回给所有进程/工作人员,他们只需等待上一次锁定释放,然后再将自己的锁定放在该行上。要完成所需的锁定,请将SELECT ... LOCK IN SHARE MODE替换为SELECT ... FOR UPDATE