原子选择和更新。使行不可见或无法通过多个过程选择

时间:2019-03-01 05:05:56

标签: sql postgresql sqlalchemy rowlocking

情况:

PostgresSQL数据库。使用SQL Alchemy ORM的应用程序(不是很重要)。具有数百万行的表。

数百个进程使用该表访问数据库。每个人都希望选择一行并根据其内容执行相对昂贵的操作,然后填充其他表并更新该行。

我使用的天真的方法是这样的:

SELECT * FROM table WHERE status = 'free';

,然后紧接着:

UPDATE table SET status 'in_process';

现在的问题是这些操作不是原子操作,这意味着在SELECTUPDATE之间的时间里,最多还有5个其他进程可以选择该行并开始处理(该行,我提醒您,它非常昂贵)。

现在我知道有SELECT FOR UPDATE可以锁定行。但这会锁定它们FOR UPDATE(duh),不会禁止选择行。

所以我想这肯定是一个非常普遍的问题,但是使用谷歌搜索并没有太大帮助。

2 个答案:

答案 0 :(得分:0)

SELECT ... FOR UPDATE是一种很好的技术,因为它们彼此阻塞,因此只有具有相同意图的其他人才能在完成交易之前获得您的行。

如果要忽略其他人锁​​定的行,则可以添加SKIP LOCKED子句。

一种可能对您有吸引力的替代方法是

UPDATE atable
SET status = 'in_progress'
WHERE status = 'free'
RETURNING *;

答案 1 :(得分:0)

似乎是解决此问题的一种方法:

使用python和sqlalchemy(但这不是必需的,因为无论如何我都使用原始SQL)

from sqlalchemy import text
sql = text("UPDATE table 
            SET status = 'in_process' 
            WHERE column.id = (SELECT column.id 
                               FROM table 
                               WHERE status='free' 
                               AND pg_try_advisory_xact_lock(column.id) 
                               LIMIT 1 FOR UPDATE) 
            RETURNING *"
row = next(iter(engine.execution_options(autocommit=True).execute(sql)))
# Now row is a tuple of values