Раздувание временных таблиц pg_attribute

Я использую COPY для вставки больших пакетов данных в нашу базу данных из CSV. Вставка выглядит примерно так:

-- This tmp table will contain all the items that we want to try to insert
CREATE TEMP TABLE tmp_items
(
    field1 INTEGER NULL,
    field2 INTEGER NULL,
    ...
) ON COMMIT DROP;

COPY tmp_items(
    field1,
    field2,
    ...
) FROM 'path\to\data.csv' WITH (FORMAT csv);

-- Start inserting some items
WITH newitems AS (
    INSERT INTO items (field1, field2)
    SELECT tmpi.field1, tmpi,field2
    FROM tmp_items tmpi
    WHERE some condition

    -- Return the new id and other fields to the next step
    RETURNING id AS newid, field1 AS field1
)
-- Insert the result into another temp table
INSERT INTO tmp_newitems SELECT * FROM newitems;

-- Use tmp_newitems to update other tables
etc....

Когда затем будут использоваться данные в tmp_items для выполнения нескольких вставок в несколько таблиц. Мы проверяем наличие дубликатов и обрабатываем данные несколькими способами перед вставкой, поэтому не все в tmp_items будет использоваться или вставляться как есть. Мы делаем это с помощью комбинации CTE и большего количества временных таблиц.

Это работает очень хорошо и достаточно быстро для наших нужд. Мы делаем их множество, и проблема заключается в том, что pg_attribute очень быстро раздувается, а автоочистка, похоже, не справляется (и потребляет много ресурсов ЦП).

Мои вопросы:

  1. Можно ли выполнить такую ​​вставку без использования временных таблиц?
  2. Если нет, то должны ли мы просто сделать автоочистку pg_attribute более агрессивной? Не будет ли это занимать столько же или больше ресурсов процессора?

person Joel    schedule 16.05.2018    source источник
comment
Вы можете полностью избавиться от временной таблицы и отобразить CSV-файл как внешнюю таблицу, используя file_fdw внешняя оболочка данных   -  person a_horse_with_no_name    schedule 16.05.2018
comment
Не в курсе, но есть ли в вашей базе данных какие-либо логические слоты репликации? В прошлом у меня были проблемы с автоочисткой таблиц каталога (в частности, pg_attribute), потому что некоторые потерянные логические слоты все еще нуждались в них.   -  person Nick Barnes    schedule 16.05.2018
comment
@NickBarnes Не то, чтобы я знал. Я могу вручную VACUUM FULL избавиться от мертвых строк. Просто кажется, что автопылесос их всех не удалит   -  person Joel    schedule 16.05.2018
comment
@a_horse_with_no_name Я немного упростил. В другой части приложения мы также используем FROM STDIN (FORMAT BINARY), а также из CSV, так что я думаю, что в этом случае это не сработает?   -  person Joel    schedule 16.05.2018


Ответы (2)


Лучшим решением было бы создание временных таблиц в начале сеанса с помощью

CREATE TEMPORARY TABLE ... (
   ...
) ON COMMIT DELETE ROWS;

Тогда временные таблицы будут сохраняться на время сеанса, но очищаться при каждой фиксации.

Это значительно уменьшит раздувание pg_attribute, и раздувание больше не должно быть проблемой.

Вы также можете присоединиться к темной стороне (будьте осторожны, это не поддерживается):

  • Запустите PostgreSQL с помощью

    pg_ctl start -o -O
    

    так что вы можете изменить системные каталоги.

  • Подключиться как суперпользователь и запустить

    UPDATE pg_catalog.pg_class
    SET reloptions = ARRAY['autovacuum_vacuum_cost_delay=0']
    WHERE oid = 'pg_catalog.pg_attribute'::regclass;
    

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

Имейте в виду, что этот параметр исчезнет после серьезного обновления.

person Laurenz Albe    schedule 16.05.2018
comment
Спасибо. Я попробую это. Как это будет работать с параллелизмом, пулами соединений и тому подобными вещами? Рискну ли я повторно использовать одну и ту же таблицу (и удалять строки) между разными вставками? - person Joel; 17.05.2018
comment
Да, и это потребует некоторого размышления, чтобы сделать это правильно. Но поскольку таблица очищается в конце каждой транзакции, это не должно быть слишком сложно. - person Laurenz Albe; 17.05.2018
comment
Ок, отлично. Я думаю, что добавление какого-то сгенерированного столбца идентификатора транзакции в временную таблицу должно охватывать это тогда - person Joel; 17.05.2018
comment
Не забывайте, что есть еще CREATE TEMPORARY TABLE IF NOT EXISTS. - person Laurenz Albe; 17.05.2018
comment
Я думал об этом. Если строки удаляются при фиксации транзакции. Тогда строки другой транзакции, использующей ту же временную таблицу, также будут удалены? Возможно, это большая проблема. Или никогда не будет двух транзакций, использующих одну и ту же временную таблицу одновременно? Спасибо за помощь в этом вопросе :) - person Joel; 22.05.2018
comment
Каждый сеанс имеет свою собственную временную таблицу; они никогда не делятся. Так как один сеанс базы данных может одновременно иметь только одну транзакцию базы данных, проблем нет. - person Laurenz Albe; 22.05.2018
comment
Окей, звучит хорошо! Но тогда мне интересно, что вы имеете в виду в своем первом комментарии: Да, и это потребует некоторого размышления, чтобы сделать это правильно...? - person Joel; 22.05.2018
comment
Я имею в виду в связи с пулом соединений. Вы должны создать временную таблицу, когда соединение с базой данных установлено, но не когда вы получаете сеанс из пула. - person Laurenz Albe; 22.05.2018
comment
А, хорошо, я вижу. Я немного запутался :) Но это не должно быть проблемой, если я использую CREATE TEMPORARY TABLE IF NOT EXISTS в запросе, который я запускаю в транзакции, я думаю - person Joel; 22.05.2018
comment
Да, я согласен. Он должен быть прост в обращении. - person Laurenz Albe; 22.05.2018
comment
Хорошо! Я попробую. Спасибо за помощь! - person Joel; 22.05.2018

Я знаю, что это старый вопрос, но кто-то может найти мою помощь здесь полезной в будущем.

Таким образом, мы очень тяжело работаем с временными таблицами, имеющими> 500 rps и асинхронным вводом-выводом через nodejs, и, таким образом, из-за этого мы столкнулись с очень сильным раздуванием pg_attribute. Все, что у вас осталось, это очень агрессивная очистка, которая останавливает производительность. Все приведенные здесь ответы не решают эту проблему, потому что удаление и воссоздание временной таблицы сильно раздувает pg_attribute, и поэтому одним солнечным утром вы обнаружите, что производительность db мертва, а pg_attribute 200+ ГБ, в то время как ваш db будет около 10 ГБ.

Итак, решение элегантно это

create temp table if not exists my_temp_table (description) on commit delete rows;

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

не забывай

vacuum full pg_depend;
vacuum full pg_attribute;

Ваше здоровье :)

person Ivan Kolyhalov    schedule 08.04.2019