3

Итак, я запутался в обработке ограничений внешних ключей в Postgresql. (версия 8.4.4, для чего это стоит).Postgresql: Неявное обнаружение блокировки от оценки ограничения внешнего ключа

У нас есть несколько таблиц, мягко анонимизированные ниже:

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 таблице, и это своего рода дорогой замок, чтобы получить.)

ответ

0

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

+0

Но почему это необходимо для проверки ограничений внешнего ключа? Я не изменяю столбцы внешнего ключа. – fennec

2

Заявление lock table device in exclusive mode принимает очень ограничительный замок на столе ("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 против одновременных обновлений, вам может потребоваться менее строгий режим блокировки. manual предлагает «эксклюзивную эксклюзивную строку» для этого вида деятельности. Хотя это всего лишь один уровень от «эксклюзивного», он совместим с оператором «select ... for share».

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

+0

Очень интересно, что это поведение не кажется нормой для Postgres. (У меня сложилось впечатление, что это было). Поэтому я посмотрю, смогу ли я обмануть таблицы и посмотреть, что их заставляет. И, да, «эксклюзивный режим», который я использовал, заключался в том, чтобы изолировать проблему, действительно, не то, что мне нравится делать регулярно. Реальные проблемы возникают, когда в инструкциях 'update' конфликтуют с другими блокировками, разбросанными по« устройству ». – fennec

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

  • Нет связанных вопросов^_^