У нас есть база данных, содержащая статистические данные. Таблицы секционируются с использованием наследования по времени. Новые дочерние таблицы создаются по мере необходимости в соответствии с поступающими данными, и приложение выполняет ночное задание, удаляющее старые дочерние таблицы.
Для обеспечения согласованности все дочерние таблицы, связанные с одним периодом времени, удаляются внутри одной транзакции.
Мы обнаруживаем, что теперь возникают взаимоблокировки между последовательностью DROP и обычными запросами SELECT. Приведенный ниже (значительно упрощенный) пример иллюстрирует проблему:
DDL и вставить фиктивную строку
CREATE TABLE a(id serial primary key, t timestamp with time zone, i int); CREATE TABLE a1 () inherits (a); CREATE TABLE a2 () inherits (a); CREATE TABLE b(id serial primary key, id_a int, x int, y int); CREATE TABLE b1 () inherits (b); CREATE TABLE b2 () inherits (b); INSERT INTO a1(t,i) VALUES (CURRENT_TIMESTAMP, 100); INSERT INTO b1(id_a,x,y) VALUES (1,200,300);
В одном сеансе psql выполните следующее (имитируя длительный запрос):
SELECT pg_sleep(90) FROM b LEFT JOIN a on a.id=b.id_a;
Во втором сеансе psql выполните следующее:
BEGIN; DROP TABLE a2; DROP TABLE b2;
В третьем сеансе запустите это (тот же запрос, что и в первом сеансе):
SELECT pg_sleep(90) FROM b LEFT JOIN a on a.id=b.id_a;
Когда запрос, запущенный на шаге 2, завершается или прерывается, возникает взаимоблокировка.
DETAIL: Process 19894 waits for AccessExclusiveLock on relation 129716 of database 44449; blocked by process 20017.
Process 20017 waits for AccessShareLock on relation 129700 of database 44449; blocked by process 19894.
Проблему легко обнаружить: блокировки, взятые транзакцией DROP, расположены в порядке a2, затем b2, но блокировки устанавливаются в обратном порядке операторами SELECT. Кажется, что операторы SELECT берут свои блокировки в порядке соединений в запросе.
Мы попытались заблокировать таблицы с помощью одной команды LOCK перед их удалением: это не помогло, они блокируются в том порядке, в котором они перечислены в команде LOCK, и взаимоблокировка все еще возникает, если это не совпадает с порядком, в котором они появляются. все запросы SELECT.
Мы не хотим навязывать определенный порядок соединения для всех пользовательских запросов только для того, чтобы гарантировать отсутствие взаимоблокировок при истечении срока действия старых данных.
Во-первых, мы обнаружили, что в зависимости от запроса SELECT, который мы пытаемся выполнить, мы получаем гораздо худшую производительность при использовании одного порядка соединения по сравнению с другим (я знаю, что этого не должно быть, но планировщик выбирает лучший план, когда мы используем один способ соединения). порядок по сравнению с другим).
Во-вторых, запросы генерируются динамически в зависимости от пользовательского ввода, и соблюдение порядка соединения по всем направлениям ограничило бы количество запросов, которые могут быть выполнены (реальные запросы намного сложнее и могут включать множество таблиц, представлений, подзапросов и т. д.). так далее).
Я прав, предполагая, что порядок соединения определяет порядок, в котором берутся блокировки для запроса SELECT?
Есть ли способ избежать этих взаимоблокировок без применения определенного порядка соединения для запросов SELECT только для чтения?
Мы используем Postgresql 9.6.6.