我使用PostgreSQL作为作业队列。以下是我检索作业并更新其状态的查询:
UPDATE requests AS re
SET
started_at = NOW(),
finished_at = NULL
FROM (
SELECT
_re.*
FROM requests AS _re
WHERE
_re.state = 'pending'
AND
_re.started_at IS NULL
LIMIT 1
FOR UPDATE SKIP LOCKED
) AS sub
WHERE re.id = sub.id
RETURNING
sub.*
现在,我有几台机器,在每台机器上我有一个带有多个线程的进程,并且在每个线程上我都有一个worker。同一进程中的所有工作程序共享一个连接池,通常具有10-20个连接。
问题是,上面的查询会多次返回一些行!
我找不到任何理由。有人可以帮忙吗?
更详细一点,我使用的是Python3和psycopg2。
更新
我试过@ a_horse_with_no_name的回答,但似乎行不通。
我注意到,一个请求被两个查询检索,started_at
更新为:
2016-04-21 14:23:06.970897 + 08
和
2016-04-21 14:23:06.831345 + 08
仅相差0.14s。
我想知道当这两个连接执行内部SELECT子查询时,两个锁都没有建立吗?
更新
更确切地说,我在一台机器上的一个过程中有200名工人(即200个线程)。
答案 0 :(得分:2)
请注意,如果您不希望他们互相介入,每个帖子都必须拥有自己的连接。
如果您的应用程序使用多个执行线程,则不能 同时共享连接。您必须明确控制 访问连接(使用互斥锁)或为每个连接使用连接 线。如果每个线程都使用自己的连接,则需要使用 AT子句指定线程将使用哪个连接。
来自:http://www.postgresql.org/docs/9.5/static/ecpg-connect.html
如果两个线程共享相同的连接,则会发生各种奇怪的事情。我相信这就是你的情况。如果使用一个连接进行锁定,则使用相同连接的所有其他线程将可以访问锁定的对象。
请允许我建议一种替代方法,这非常简单。使用redis作为队列。您可以简单地使用redis-py和lpush / rpop方法,也可以使用python-rq。
答案 1 :(得分:1)
有可能在select时发出锁定事务,或者在select的结果准备好并且update语句开始时锁定丢失。您是否尝试过明确开始交易?
BEGIN;
WITH req AS (
SELECT id
FROM requests AS _re
WHERE _re.state = 'pending' AND _re.started_at IS NULL
LIMIT 1 FOR UPDATE SKIP LOCKED
)
UPDATE requests SET started_at = NOW(), finished_at = NULL
FROM req
WHERE requests.id = req.id;
COMMIT;