Есть ли в PostgreSQL несколько ОБНОВЛЕНИЙ для разных строк в одной таблице, имеющих конфликт блокировки?
Я немного интересуюсь обновлением, я делаю один большой стол, нужно ли мне беспокоиться о блокировках.
У меня есть таблица, похожая на это:
CREATE TABLE "ItemsToProcess"(
"id" text,
"WorkerInstanceId" text,
"ProcessingStartTime" timestamp with time zone,
"UpdatedTime" timestamp with time zone,
CONSTRAINT "ITP_PK" PRIMARY KEY ("id")
)WITH (
OIDS=FALSE
);
Первоначально эта таблица содержит ~2,0 миллиона строк, и только заполненный идентификатор, WorkerInstanceId и две метки времени по умолчанию равны нулю и находятся в начале цикла.
То, что происходит, - то, что некоторые рабочие приложения (по крайней мере два, но будут приблизительно 10-13 в производстве) отметят партию идентификаторов (я планирую установить batchSize в 200) из этой таблицы от них, чтобы обработать. То, что происходит во время обработки, сейчас не имеет значения. Маркировка партии выглядит следующим образом:
UPDATE "ItemsToProcess"
SET "WorkerInstanceId" = ?, "ProcessingStartTime" = current_timestamp()
WHERE "WorkerInstanceId" is NULL
LIMIT 200;
Мой вопрос: нужно ли беспокоиться о блокировке строк, которые я собираюсь обновить, прежде чем делать обновление?
Документация Postgres гласит:
ROW EXCLUSIVE
Конфликты с режимами блокировки SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE и ACCESS EXCLUSIVE.
Команды UPDATE, DELETE и INSERT получают этот режим блокировки для целевой таблицы (в дополнение к блокировкам ACCESS SHARE для любых других ссылочных таблиц). В общем, этот режим блокировки будет активирован любой командой, которая изменяет данные в таблице.
Поэтому я думаю, что всякий раз, когда один из работников делает это обновление, вся таблица блокируется, обновляется 200 строк, и в конце концов блокировка освобождается. Пока замок не будет на месте, другие рабочие ждут, чтобы замок освободился. Я прав или я что-то пропустил?
Спасибо за помощь!
2 ответа
Вы упускаете пару вещей.
Во-первых, PostgreSQL не предлагает опцию "LIMIT" для обновления. Смотрите документы для обновления.
Во-вторых, обратите внимание, что "ROW EXCLUSIVE" не конфликтует с самим собой, он конфликтует с "SHARE ROW EXCLUSIVE", который отличается. Таким образом, ваши операторы UPDATE могут безопасно выполняться одновременно от нескольких работников. Вы все еще хотите, чтобы ваши времена ОБНОВЛЕНИЯ были низкими. Тем не менее, у вас уже есть встроенный способ настроить это, уменьшив ваш "batchSize", если у вас возникнут проблемы.
UPDATE
блокирует строку, поэтому вам не нужно сначала ее блокировать. Если вы попытаетесь UPDATE
перекрывающиеся наборы строк одновременно, второй UPDATE
будет ждать первой транзакции для подтверждения или отката.
Большая проблема с вашим подходом - кроме того, что UPDATE
не имеет LIMIT
оговорка - это то, что несколько рабочих будут пытаться захватить одни и те же строки. Вот что происходит:
worker1: фильтрует таблицу, чтобы найти 200 строк, и блокирует их. worker1: начинает обновление строк. worker2: фильтрует таблицу, чтобы найти 200 строк. worker2: пытается начать обновление строк, но выбрал те же строки, что и worker1, поэтому блокирует блокировку worker1. Завершает обновление строк worker2: после снятия блокировки повторно проверяет условие WHERE и обнаруживает, что ни одна из строк больше не соответствует, потому что worker1 обновил их. Обновляет ноль строк. ... и повтори!
Вам нужно либо:
- Имейте центральную очередь, раздающую строки надлежащим безопасным способом параллелизма; или же
- Назначьте работникам непересекающиеся диапазоны идентификаторов для работы на
Что касается LIMIT
- вы могли бы использовать WHERE id IN (SELECT t.id FROM thetable t LIMIT 200 ORDER BY id)
- но у вас будет одна и та же проблема, если оба сотрудника выберут один и тот же набор строк для обновления.