2015-11-17 3 views
0

Возможно ли создать блокировку для определенного значения в инструкции INSERT в PostgreSQL?Блокировка PostgreSQL INSERTS с определенными значениями?

Позволяет сказать, у меня есть эта таблица:

CREATE TABLE IF NOT EXISTS bookings (
bookingID SERIAL PRIMARY KEY, 
tableID  integer REFERENCES tables ON DELETE CASCADE, 
startTime timestamp NOT NULL, 
endTime  timestamp NOT NULL, 
); 

Теперь, если сделка-блок с вкладышем запускается, я могу создать блокировку для всех других вставок с тем же TABLEID и тем же днем ​​в течение сделка время?

Таким образом, второе бронирование для того же tableID должно ждать завершения первого. И после этого можно снова проверить, если INSERT все еще возможен. Значит, это в основном ROW-lock для определенного значения в INSERTS.

Программа написана на Java, но я не хочу использовать синхронизированный блок из-за узкого места.

Спасибо за помощь

+0

«* Я не хочу использовать синхронизированные-блок *» - почему вы считаете, что синхронизация на ресурсе базы данных более эффективна, чем синхронизация в коде Java? –

+0

Насколько я знаю, если я использую 'synchronized', вся таблица« заблокирована », поэтому другие транзакции должны ждать. Но, если это возможно, я хочу только блокировать части таблицы для повышения эффективности. – FrediDoe

+0

Другим вариантом является изоляция «SERIALIZABLE», но предложение EgorRogov использовать ограничения исключения является лучшим решением в этом случае. –

ответ

4

Вопрос в том, почему вы хотите, чтобы блокировать другие вставки для этой таблицы?

Для меня это похоже, что вы хотите быть уверенным, что нет пересекающихся интервалов для одного и того же идентификатора таблицы. Вероятно, вы проверяете это в коде Java, и вы не хотите, чтобы другие вставки мешали проверке.

Если это так, вам не нужны никакие замки: используйте ограничение EXCLUDE.

Для этого вам необходимо:

  1. Изменить два timestamp поля в одно tsrange поле.
  2. Установить btree_gist расширение (оно включено в вклад).

Ваша таблица будет выглядеть следующим образом:

CREATE TABLE IF NOT EXISTS bookings (
    bookingID SERIAL PRIMARY KEY, 
    tableID  integer REFERENCES tables ON DELETE CASCADE, 
    during  tsrange NOT NULL, 
    EXCLUDE using gist(during with &&, tableID with =) 
); 

Специальный индекс GIST будет создан автоматически, чтобы гарантировать, что не будет никаких пересекающихся интервалов (&& оператор) для идентичных TABLEID (= оператора).

Некоторые примеры:

-- interval for tableID=10 
test=# insert into bookings values (1, 10, '[2015-11-17 10:00, 2015-11-17 12:00)'); 
INSERT 0 1 

-- interval for tableID=11 
test=# insert into bookings values (2, 11, '[2015-11-17 10:00, 2015-11-17 12:00)'); 
INSERT 0 1 

-- can't create intersecting interval for tableID=10 
test=# insert into bookings values (3, 10, '[2015-11-17 11:00, 2015-11-17 13:00)'); 
ERROR: conflicting key value violates exclusion constraint "bookings_during_tableid_excl" 
DETAIL: Key (during, tableid)=(["2015-11-17 11:00:00","2015-11-17 13:00:00"), 10) conflicts with existing key (during, tableid)=(["2015-11-17 10:00:00","2015-11-17 12:00:00"), 10). 

-- ok to create non-intersecting interval 
test=# insert into bookings values (4, 10, '[2015-11-17 12:00, 2015-11-17 13:00)'); 
INSERT 0 1 
1

Согласен с @a_horse_with_no_name. Конечно, синхронизированный блок будет лучшим решением.

Но если вы настаиваете на использовании блокировок базы данных, то advisory locks (too) может вам помочь.

Например:

begin; -- we will use transaction level lock but there is an option with session level locks 
select pg_advisory_xact_lock(123); -- 123 is a tableID you need to lock 
insert into bookings(tableID, ...) values(123, ...); -- or/and other queries 
commit; 
2

То, что вы хотите, называется предикатом замок. Он не поддерживается напрямую PostgreSQL, но консультативная блокировка, описанная @ stas.yaranov, является хорошим способом. Как указывает @EgorRogov, если это возможно, вы должны полностью устранить необходимость блокировки с помощью соответствующих ограничений.

Другой вариант, о котором никто не упомянул, заключается в использовании изоляции транзакций SERIALIZABLE в PostgreSQL 9.1 или новее.Это использует метод управления параллелизмом, подобный оптимистичной блокировке, где каждая транзакция продолжается без блокировки, но одна из них может прерваться, если она конфликтует с другой. Это означает, что ваше приложение должно быть готово ловить ошибки с ошибками сериализации и повторять транзакции, но обычно это очень эффективный и простой способ справиться с такими вещами - особенно в тех случаях, когда ограничения на исключение не помогут вам.

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

(Вы не можете использовать SERIALIZABLE для этого в 9.0 или старше, кстати, так как это не достаточно умны, в этих версиях)

Смежные вопросы