Postgresql: неявное получение блокировки из оценки ограничения внешнего ключа - PullRequest
3 голосов
/ 14 июня 2010

Итак, меня смущает обработка ограничений внешнего ключа в 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.

Мне кажется странным, что обновление таблицы 'left' в 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 имеется лот доступа, и это своего рода дорогая блокировка, которую можно получить.)

Ответы [ 2 ]

2 голосов
/ 15 июня 2010

Оператор lock table device in exclusive mode принимает очень ограничительную блокировку таблицы ( «эксклюзивный режим» ).Изменение таблицы с внешним ключом для родительской таблицы требует довольно безобидной блокировки общего ресурса для родительской таблицы (например, вы не можете обрезать таблицу, когда строки, ссылающиеся на нее, потенциально обновляются).

На самом деле, пробуя это сейчас, я не могу воспроизвести ваше поведение блокировки (на 8.4.4, как вы).Я сделал:

create table device(device_id serial primary key, value text not null);
create table device_foo(device_foo_id serial primary key, device_id int not null references device(device_id) on delete cascade, value text not null);
insert into device(value) values('FOO'),('BAR'),('QUUX');
insert into device_foo(device_id, value) select device_id, v.value from (values('mumble'),('grumble'),('fumble')) v(value), device;

И затем в двух одновременных соединениях я сделал:

<1>=# begin; lock table device in exclusive mode;
<2>=# begin; update device_foo set value = value || 'x';

Мне кажется, это эквивалентно тому, что вы делаете, но я не понимаюблокировка второго сеанса - он немедленно выдает «ОБНОВЛЕНИЕ 9», как и ожидалось. Вставка в блоки device_foo, как и следовало ожидать, как и оператор обновления, устанавливающий столбец device_id.Я могу видеть ExclusiveLock в pg_locks из сеанса db1 в сеансе db2.Это также блокирует, если я делаю «выбрать * с устройства для обмена», что является утверждением, которое вы видите в ошибке взаимоблокировки.Я также не получаю взаимоблокировку, если я делаю "select * from device_foo для обновления" из соединения db1, в то время как db2 блокируется, пытаясь обновить столбец device_id в device_foo.

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

Чтобы заблокировать таблицу device от одновременных обновлений, вы можетехотите менее строгий режим блокировки.В руководстве предлагается "поделиться строкой эксклюзивно" для этого вида деятельности.Хотя это только на один уровень ниже «эксклюзивного», оно совместимо с утверждением «выберите ... для обмена».

Так что, на самом деле, открытый вопрос - что выдает этот «выбор ...»для обмена "запрос?: -S Это похоже на утверждение, предназначенное для утверждения целостности внешнего ключа, но я не могу воспроизвести его.

0 голосов
/ 15 июня 2010

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

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