Иметь блок «выбрать для обновления» на несуществующих строках - PullRequest
16 голосов
/ 05 августа 2011

у нас есть некоторые постоянные данные в приложении, которые запрашиваются с сервера и затем сохраняются в базе данных, чтобы мы могли отслеживать дополнительную информацию. Поскольку мы не хотим запрашивать, когда объект используется в памяти, мы делаем select for update, чтобы другие потоки, которые хотят получить те же данные, были заблокированы.

Я не уверен, как select for update обрабатывает несуществующие строки. Если строка не существует и другой поток попытается выполнить еще один select for update в той же строке, будет ли этот поток заблокирован до завершения другой транзакции или он также получит пустой набор результатов? Если он получает только пустой набор результатов, есть ли способ также заблокировать его, например, сразу вставив пропущенную строку?

EDIT:

Поскольку было замечание, что мы могли бы заблокировать слишком много, вот еще некоторые подробности о конкретном использовании в нашем случае. В сокращенном псевдокоде наш программный поток выглядит так:

d = queue.fetch();
r = SELECT * FROM table WHERE key = d.key() FOR UPDATE;
if r.empty() then
  r = get_data_from_somewhere_else();

new_r = process_stuff( r );


if Data was present then
   update row to new_r
else
   insert new_r

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

EDIT:

Пока у меня есть следующее решение, которое мне кажется уродливым.

select the data for update
if zero rows match then
  insert some dummy data   // this will block if multiple transactions try to insert
  if insertion failed then
    // somebody beat us at the race
    select the data for update

do processing

if data was changed then
   update the old or dummy data
else
   rollback the whole transaction

Однако я не уверен на 100%, что это действительно решает проблему, и при этом это решение не кажется хорошим стилем. Так что если кто-то может предложить что-то более полезное, это было бы здорово.

Ответы [ 3 ]

16 голосов
/ 06 августа 2011

Я не уверен, как выбрать для обновления обрабатывает несуществующие строки.

Это не так.

Лучшее, что вы можете сделать, - это использовать консультативную блокировку, если вы знаете что-то уникальное в новой строке. (При необходимости используйте hashtext () и oid таблицы, чтобы заблокировать его.)

Следующая лучшая вещь - это блокировка стола.

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

0 голосов
/ 01 ноября 2016

Пример решения (я не нашел лучшего: /)

Тема A:

BEGIN;
SELECT pg_advisory_xact_lock(42); -- database semaphore arbitrary ID
SELECT * FROM t WHERE id = 1;
DELETE FROM t WHERE id = 1;
INSERT INTO t (id, value) VALUES (1, 'thread A');
SELECT 1 FROM pg_sleep(10); -- only for race condition simulation
COMMIT;

Резьба B:

BEGIN;
SELECT pg_advisory_xact_lock(42); -- database semaphore arbitrary ID
SELECT * FROM t WHERE id = 1;
DELETE FROM t WHERE id = 1;
INSERT INTO t (id, value) VALUES (1, 'thread B');
SELECT 1 FROM pg_sleep(10); -- only for race condition simulation
COMMIT;

Вызывает всегда правильный порядок выполнения транзакций.

0 голосов
/ 09 августа 2011

Глядя на код, добавленный во втором редактировании, он выглядит правильно.

Что касается хака, то есть пара вариантов - в основном все дело в перемещении логики базы данных в базу данных.

Нужно просто поместить целое , выбранное для обновления, если оно не существует, затем вставить в функцию логику и вместо нее выполнить select get_object(key1,key2,etc).

В качестве альтернативы, вы можететриггер вставки, который будет игнорировать попытки добавить запись, если она уже существует, и просто выполнить вставку, прежде чем выполнять выбор для обновления.Это, однако, имеет больше возможностей для вмешательства в уже существующий код.

(Если я не забуду, я позже отредактирую и добавлю пример кода, когда смогу проверить, что яя делаю.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...