我在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 MODE
或SELECT ... 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事务到两个sql控制台时,我可以看到后面的事务如何等待第一个完成。 这种效果破坏了拥有多个工人的完整想法,因为他们最终会被交易同步。
如何在不阻止的情况下从其他交易“锁定”一行?
答案 0 :(得分:1)
您正在使用LOCK IN SHARE MODE
锁定来自UPDATE
或DELETE
操作的行,但允许读取该行。有关详细信息,请参阅this documentation。文档的简要说明如下。
SELECT ... LOCK IN SHARE MODE在任何读取的行上设置共享模式锁定。其他会话可以读取行,但在事务提交之前无法修改它们。
对于搜索遇到的索引记录,SELECT ... FOR UPDATE阻止其他会话执行SELECT ... LOCK IN SHARE MODE或从某些事务隔离级别读取。
您的查询将同一行返回给所有进程/工作人员,他们只需等待上一次锁定释放,然后再将自己的锁定放在该行上。要完成所需的锁定,请将SELECT ... LOCK IN SHARE MODE
替换为SELECT ... FOR UPDATE
。