Атомный выбор и обновление.Сделать строку невидимой или не выбираемой несколькими процессами - PullRequest
0 голосов
/ 01 марта 2019

Ситуация:

База данных PostgresSQL.Приложение с SQL Alchemy ORM (не очень важно).Таблица с миллионами строк.

Сотни процессов обращаются к базе данных с этой таблицей.Каждый хочет выбрать одну строку и выполнить относительно дорогую операцию на основе своего содержимого, затем заполнить другие таблицы и обновить эту строку.

Наивный подход, который я использовал, выглядит следующим образом:

SELECT * FROM table WHERE status = 'free';

а затем сразу после этого:

UPDATE table SET status 'in_process';

Теперь проблема заключается в том, что эти операции не являются атомарными, то есть за время между SELECT и UPDATE могут выбрать до 5 других процессов.этот ряд и начинайте работать над ним (что, напомню, довольно дорого).

Теперь я знаю, что есть SELECT FOR UPDATE, который блокирует строки.Но он блокирует их FOR UPDATE (дух), он не запрещает выделение строк.

Так что я думаю, это должно быть довольно распространенной проблемой, но поиск в Google не сильно помог.

Ответы [ 2 ]

0 голосов
/ 01 марта 2019

Похоже, это способ решить эту проблему:

Использование python и sqlalchemy (но это не обязательно, так как я все равно использую raw 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
0 голосов
/ 01 марта 2019

SELECT ... FOR UPDATE - хороший метод, потому что они блокируют друг друга, поэтому никто другой с тем же намерением не может получить вашу строку, пока ваша транзакция не будет завершена.

Вы можете добавить предложение SKIP LOCKED, если выхотите игнорировать строки, заблокированные другими.

Альтернативой, которая может быть привлекательной для вас, является

UPDATE atable
SET status = 'in_progress'
WHERE status = 'free'
RETURNING *;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...