Привет, Хабр!
Пакетная обработка данных — это метод выполнения большого числа операций над данными за одно действие, а не по отдельности. С пакетной обработкой можно уменьшить время выполнения некоторых операций.
В PostgreSQL пакетная обработка реализуется с помощью массовых вставок, обновлений, удалений или переноса данных между таблицами .
В этой статье рассмотрим, как реализовать пакетную обработку в PostgreSQL.
Способы пакетной обработки данных
Команда COPY
- самый хороший способ массового ввода и вывода данных. Она позволяет быстро загружать данные из файла в табличку БД или экспортировать данные из таблицы в файл. COPY
использует высокопроизводительный протокол для перемещения данных между файлом и таблицей:
COPY my_table (column1, column2, column3)
FROM '/path/to/file.csv'
WITH (FORMAT csv, HEADER true, DELIMITER ',');
Тут данные из файла file.csv
загружаются в таблицу my_table
. Опции FORMAT csv
, HEADER true
и DELIMITER ','
указывают на формат файла, наличие заголовков и разделитель столбцов соответственно.
COPY
поддерживает различные форматы файлов, а также может использоваться в транзакциях.
Временные таблицы TEMPORARY
или TEMP
могут быть использованы для промежуточного хранения данных.
Пример:
BEGIN;
-- создание временной таблицы
CREATE TEMP TABLE temp_table AS
SELECT id, new_value
FROM source_table;
-- обновление основной таблицы на основе временной
UPDATE main_table
SET column_name = temp_table.new_value
FROM temp_table
WHERE main_table.id = temp_table.id;
-- удаление записей на основе временной таблицы
DELETE FROM main_table
USING temp_table
WHERE main_table.id = temp_table.id;
COMMIT;
Временная таблица temp_table
используется для обновления и удаления записей в основной таблице main_table
.
Для вставки большого количества записей можно использовать многоразовые вставки, которые выполняются в одном запросе, например:
INSERT INTO my_table (column1, column2, column3)
VALUES
(value1_1, value1_2, value1_3),
(value2_1, value2_2, value2_3),
(value3_1, value3_2, value3_3);
Для операций посложней можно использовать процедуры на PL/pgSQL:
DO $$
BEGIN
FOR i IN 1..1000 LOOP
INSERT INTO my_table (column1, column2)
VALUES (i, i * 2);
END LOOP;
END $$;
Так можно вставить 1000 записей в таблицу my_table
в рамках одной процедуры.
Преимущества использования многоразовых вставок и PL/pgSQL:
Сокращение накладных расходов на обработку транзакций.
Повышение производительности за счет пакетной обработки данных.
Как все это дело оптимизировать
Перед выполнением массовых операций рекомендуется временно отключить индексы на целевой табличке, т.к обновление индексов при каждой вставке или обновлении строки существенно замедляет процесс. После завершения операции индексы можно пересоздать.
Отключаем индексы:
DROP INDEX index_name1, index_name2, ...;
Выполняем массовую операцию, например вставку данных.
Восстанавливаем индексы:
CREATE INDEX index_name1 ON table_name(column1);
CREATE INDEX index_name2 ON table_name(column2);
Триггеры запускаются при вставке или удалении записей, могут значительно замедлить массовые операции, т.к они требуют выполнения доп. логики после каждой операции.
Отключаем триггеры:
ALTER TABLE table_name DISABLE TRIGGER ALL;
После выполнения массовых операций включаем с помощью ENABLE
.
Плюсом можно подключить нелогируемые таблицы и поработать с настройкой work_mem.
Примеры
Массовая вставка с использованием команды COPY и временной деактивации индексов:
-- отключение индексов перед вставкой данных
DROP INDEX IF EXISTS idx_column1, idx_column2;
-- массовая вставка данных с использованием команды COPY
COPY target_table (column1, column2)
FROM '/path/to/your/file.csv'
WITH (FORMAT csv, HEADER true);
-- восстановление индексов после вставки данных
CREATE INDEX idx_column1 ON target_table (column1);
CREATE INDEX idx_column2 ON target_table (column2);
Пакетное обновление данных с использованием временной таблички:
BEGIN;
-- создание временной таблицы
CREATE TEMP TABLE temp_updates AS
SELECT id, new_value FROM source_table;
-- обновление основной таблицы на основе данных из временной таблицы
UPDATE target_table
SET column_to_update = temp_updates.new_value
FROM temp_updates
WHERE target_table.id = temp_updates.id;
COMMIT;
Пакетное удаление данных с PL/pgSQL:
BEGIN;
-- отключение всех триггеров на целевой таблице
ALTER TABLE target_table DISABLE TRIGGER ALL;
-- пакетное удаление данных с использованием PL/pgSQL
DO $$
DECLARE
r RECORD;
BEGIN
FOR r IN (SELECT id FROM source_table WHERE condition) LOOP
DELETE FROM target_table WHERE id = r.id;
END LOOP;
END $$;
-- включение всех триггеров на целевой таблице
ALTER TABLE target_table ENABLE TRIGGER ALL;
COMMIT;
Больше про обработку данных, инструменты аналитики и не только, эксперты OTUS рассказывают в рамках практических онлайн-курсов. С полным каталогом курсов можно ознакомиться по ссылке.
Комментарии (18)
0pauc0
25.06.2024 18:25+9Пересоздать индекс, отключить триггеры? Вы серьезно? На рабочей базе?
lampa
25.06.2024 18:25Если БД используется как olap, то всё это рабочие схемы. Переливки во временные таблицы, отключение репликации, расчёт на партициях, блин да даже ram диск использовать это вполне норм. Да, тогда надо брать спарк, но тут речь про постгрю :-)
0pauc0
25.06.2024 18:25Если для статики, то наверное и возможно, в том числе и на PG, если динамическая часть тоже на нём. Но откуда в статике триггеры?
Batalmv
25.06.2024 18:25+9По ходу автор только в песочнице копировал данные, так как на живой базе отключение индексов == можно всем идти на обед, а деактивация тригерров вообще чревата потерей части бизнес логики
Ну а пакетная обработка одним update даже на уровень песочницы не тянет
Koyotter
25.06.2024 18:25+4Вот это
FOR r IN (SELECT id FROM source_table WHERE condition) LOOP DELETE FROM target_table WHERE id = r.id; END LOOP;
будет работать на порядок дольше, чем
DELETE FROM target_table WHERE id in (SELECT id FROM source_table WHERE condition)
lampa
25.06.2024 18:25Вообще если предполагается какое-то массовое удаление и речь зашла про доп. подготовки вроде отключения триггеров, то есть еще вариант - таблицу готовим в рид онли, на её основе создаем новую через `create as select` и переименовываем новую в старую, а старую потом дропаем, иначе это удаление вообще может зависнуть)))
Koyotter
25.06.2024 18:25Вот это
FOR r IN (SELECT id FROM source_table WHERE condition) LOOP DELETE FROM target_table WHERE id = r.id; END LOOP;
будет работать на порядок дольше, чем
DELETE FROM target_table WHERE id in (SELECT id FROM source_table WHERE condition)
Koyotter
25.06.2024 18:25Вот это
FOR r IN (SELECT id FROM source_table WHERE condition) LOOP DELETE FROM target_table WHERE id = r.id; END LOOP;
будет работать на порядок дольше, чем
DELETE FROM target_table WHERE id in (SELECT id FROM source_table WHERE condition)
Koyotter
25.06.2024 18:25Вот это
FOR r IN (SELECT id FROM source_table WHERE condition) LOOP DELETE FROM target_table WHERE id = r.id; END LOOP;
будет работать на порядок дольше, чем
DELETE FROM target_table WHERE id in (SELECT id FROM source_table WHERE condition)
Akina
25.06.2024 18:25+2Так можно вставить 1000 записей в таблицу
my_table
в рамках одной процедуры.Так можно только по рукам получить. Ну или по голове. generate_series() для кого придуманы?
Пакетное удаление данных с PL/pgSQL
И за это тоже бить будут. DELETE имеет опциональный USING clause. Именно для таких целей.
Koyotter
25.06.2024 18:25+2Извините за дубликаты комментариев, ошибку выдавал, а оказалось что запостило
miruzzy
25.06.2024 18:25+3Плюсом можно подключить нелогируемые таблицы и поработать с настройкой work_mem.
Что, зачем это делать, как это поможет и при каких условиях ?
DO $$ BEGIN FOR i IN 1..1000 LOOP INSERT INTO my_table (column1, column2) VALUES (i, i * 2); END LOOP; END $$;
А это что такое, почему не generate_seriaes ? ( тот же вопрос и про удаление ( коммент был выше, да и вообще, почему не транкейт ? )
Раз уж массовая производительная обработка, то почему нет `merge` ?
Просто вброс какой-то инфы, взятой из разных источников, вообще не информативно ничего.
breninsul
25.06.2024 18:25+1индексы то ладно, их переидексировать можно. Но триггеры то не просто так висят на таблице.
Круто, конечно, что у нас бизнес-логика сломается, но не очень
vmalyutin
25.06.2024 18:25+1Автор про партиции что-нибудь слышал?
Автор про мусор и вакумм что-нибудь слышал?
Можно, конечно, над таблицей и так плясать и эдак, но почему бы не ограничить доступ к таблице, если уж она никому не нужна, без индексов и тригеров-то? Я бы уж тогда и fk прибил бы. Можно ж восстановить.
Есть и другие способы избавиться/обновить записи. Но надо сначала те что выше отработать.
CHIM86
25.06.2024 18:25А есть ли вариант напрямую из xml файла раскладывать содержимое тегов или атрибутов по порядку в колонки таблицы?
Akina
25.06.2024 18:25Конечно, можно. XML - это же текст. Текстовых функций в постгрессе - вагон и маленькая тележка, распарсить можно что угодно и как угодно.
Хотя это дурь, конечно. Ибо в Постгрессе есть и горсть XML-функций. В том числе XMLTABLE().
odilovoybek
Большое спасибо, на днях чудил с криптоагрегатором и с API подгружал кучу данных, пригодится. Кажется что база, но тут есть некоторые тонкости для меня. А может кто-то порекомендовать хороший курс или книжку по Postgre?
miruzzy
https://www.postgrespro.ru/education/courses
Ну как минимум вот эти курсы довольно хороши
Из книг - ну если нужно что-то на русском языке - опять же от этих ребят есть 2 книги по постгресу. Одна у Рогова, вторая у Моргунова