Итак, меня смущает обработка ограничений внешнего ключа в Postgresql. (версия 8.4.4, чего стоит).
У нас есть пара таблиц, слегка анонимизированных ниже:
device:
(id, blah, blah, blah, blah, blah x 50)…
primary key on id
whooooole bunch of other junk
device_foo:
(id, device_id, left, right)
Foreign key (device_id) references device(id) on delete cascade;
primary key on id
btree index on 'left' and 'right'
Поэтому я отправился с двумя окнами базы данных, чтобы выполнить несколько запросов.
db1> begin; lock table device in exclusive mode;
db2> begin; update device_foo set left = left + 1;
Соединение с db2 блокируется.
Мне кажется странным, что на обновление «левого» столбца в device_stuff должна влиять активность в таблице устройств. Но это. Фактически, если я вернусь к db1:
db1> select * from device_stuff for update;
*** deadlock occurs ***
В журнале pgsql есть следующее:
blah blah blah deadlock blah.
CONTEXT: SQL statement "SELECT 1 FROM ONLY "public"."device" x WHERE "id" OPERATOR(pg_catalog.=) $1 FOR SHARE OF X: update device_foo set left = left + 1;
Я предполагаю, что у меня есть две проблемы: первая заключается в том, что я не понимаю точного механизма, с помощью которого происходит такая блокировка. У меня есть пара полезных запросов для запроса pg_locks, чтобы узнать, какие блокировки вызывает оператор, но я не смог наблюдать этот конкретный вид блокировки, когда запускал команду update device_foo
изолированно. (Возможно, я делаю что-то не так.) Я также не могу найти документацию о поведении получения блокировки при проверках ограничений внешнего ключа. Все, что у меня есть, это сообщение журнала. Должен ли я сделать из этого вывод, что любое изменение в строке получит блокировку обновления для всех таблиц, для которых оно является внешним ключом?
Вторая проблема заключается в том, что я хотел бы найти способ сделать так, чтобы этого не происходило. Я получаю случайные взаимоблокировки в реальном приложении. Я хотел бы иметь возможность запускать большие операторы обновления, которые влияют на все строки в device_foo
, не прибегая к большой блокировке таблицы устройств. (В таблице device
происходит множество операций доступа, и получить такую блокировку довольно дорого.)