Начните с этих двух таблиц и начальных записей для c
:
create table c
(
id serial primary key,
name varchar not null
);
create table e
(
id varchar not null,
c_id bigint references c (id) not null,
name varchar not null,
primary key (id, c_id)
);
insert into c (name) values ('deadlock test');
Тема 1:
begin;
select * from c where id = 1 for update;
insert into e (id, c_id, name) VALUES ('bar', 1, 'second') on conflict do nothing ;
commit;
Тема 2:
begin;
insert into e (id, c_id, name) VALUES ('bar', 1, 'first') on conflict do nothing ;
commit;
Порядок выполнения:
- Тема 1: начало
- Тема 2: начало
- Тема 1: блокировка
c
- Тема 2: вставить
e
- Тема 1: вставить
e
<- тупик </li>
Почему это происходит?
Добавление блокировки для c
в потоке 2, конечно, можно избежать тупика, но мне не понятно почему. Также интересно то, что если строка в e
существует до запуска потока 1 или 2., тогда не возникает тупиков.
Я подозреваю, что происходит как минимум две вещи:
- Первичный ключ создает уникальное ограничение, которое вызывает некоторую блокировку на
e
, которую я не понимаю, даже с ON CONFLICT DO NOTHING
. - Внешний ключ на
c_id
приводит к некоторому триггеру вызывая блокировку c
при вставке новой записи (или когда c_id
обновляется, я полагаю).
Спасибо!