TGViewer
yet another dev yet another dev @yet_another_dev · 382 subscribers
Post #206 498
Потоковая вставка данных в Postgres через денормализованные таблицы. Часть 4.

Часть 1.
Часть 2.
Часть 3.

Заключительная часть серии статей. Сегодня отвечу на вопрос, заданный в 1 части: с каким индексом на stage_table такой запрос отработает быстрее всего:

select resource, billing_date, sum(cost)
from stage_table
group by resource, billing_date


Ответ

Индекс неважен и результаты одинаковые, так как планировщик всегда выбирает HashAggregate с Seq Scan (диаграмма 1), потому что любой другой алгоритм агрегирования окажется медленнее.

Объяснение

Убедится в том, что Postgres неспроста использует HashAggregate, можно на примере, если добавить к таблице индекс и принудительно отключить HashAggregate с помощью опции enable_hashagg:

create index idx_billing_data
on billing_data (resource, billing_date)
include (cost);

set enable_hashagg = off;


С включённым HashAggregate видим знакомую картину (диаграмма 2): HashAggregate с Seq Scan для относительно небольших таблиц и Parallel HashAggregate с Seq Scan для крупных таблиц, даже несмотря на наличие индекса.

С отключённым HashAggregate Postgres выбирает GroupAggregate с Index Only Scan или Group Aggregate с Seq Scan, в зависимости от размера таблицы. Наконец-таки индекс используется, но общая производительность всё равно хуже, чем HashAggregate.

Причины следующие:

1. HashAggregate выполняет один последовательный проход по таблице и считает сумму во внутренней хеш‑таблице. Это минимизирует непоследовательный доступ, и данные попадают в кэш CPU. Для нашей задачи – сгруппировать всё содержимое таблицы – это идельный сценарий.

2. Index Only Scan медленнее из-за непоследовательного доступа. Поскольку индекс по умолчанию – это B-Tree, его чтение сопряжено с чтением данных из разных частей памяти. Индексы хороши, когда нужно получить небольшой кусочек таблицы, но не когда нужно обработать всё сразу.

3. GroupAggregate с Seq Scan требует отсортированного входа по ключам, используемым в GROUP BY. Поскольку Seq Scan, который в нашем случае ещё и выполняется параллельно, не гарантирует порядок данных, то планировщик добавляет в план сортировку. Очевидно, это ухудшает время выполнения.

Выводы

Если нужно быстро загрузить большой объём данных в Postgres с использованием промежуточных таблиц, то:

1. Не используйте индексы в промежуточных таблицах, потому что

- вы потратите время на создание индекса;
- индексы замедляют вставку в таблицу;
- индексы не дают преимуществ, если требуется агрегировать данные по всей промежуточной таблице;

2. Используйте UNLOGGED таблицы, вместо TEMP таблиц. Оба типа не используют Write-Ahead Log, но UNLOGGED позволяет распараллеливать запросы SELECT, что может значительно ускорить выполнения запросов. Главное не забыть удалить UNLOGGED таблицу в конце транзакции. Они не удаляются автоматически, как TEMP таблицы.

Важно помнить, что всё вышесказанное верно для рассматриваемого сценария: массовая вставка с последующей агрегацией по всей промежуточной таблице. Для CRUD функционала лучше использовать обычные таблицы и добавлять индексы.
  • 👍 4
More from @yet_another_dev
  1. Sep 21, 2026Опубликовал вчера ролик в одной запрещённой в России соцсети про то, как сходил на выборы.…
  2. Sep 20, 2026Мы пришли в 7:50 и очередь уже была 🥲 Пообщались с другими людьми. Многие приехали из дру…
  3. Sep 19, 2026Post #383
  4. Sep 18, 2026Последние пару недель на чат нападают боты со спамом (прикрыл стикером). Поэтому чат тепер…
  5. Sep 17, 2026Что интересного в этой статье: 1. Потрачено $120К, а агенты суммарно отработали около 3-х…
  6. Sep 17, 2026В Microsoft переписали рантайм GitHub Copilot с TypeScript на Rust при помощи агентов. Под…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →