Параллелизм скрывает стоимость тяжёлых запросов, но не устраняет её. Индексы и механизмы кэширования уменьшают объём работы, который нужно выполнить.
Параллельное выполнение всё равно делает тот же объём работы — оно лишь распределяет его между несколькими процессорами. Воркеры позволяют задействовать больше CPU, но не сокращают количество операций. Если запрос выполняет полное сканирование таблицы, он всё равно прочитает всю таблицу. Просто сделает это быстрее за счёт дополнительных CPU.
На первый взгляд безобидный запрос:
SELECT
user_id,
count(*) AS total_events
FROM events
GROUP BY user_id
ORDER BY total_events DESC
LIMIT 10;
Предположим, что индексов нет, таблица
events содержит 1 000 000 строк и 10 000 уникальных user_id. Такой запрос выполняет большой объём работы. В любом случае ему придётся прочитать каждую строку таблицы.Параллельный конвейер Postgres
Postgres разделил таблицу на три части, выполнил агрегацию параллельно и затем объединил результаты.
[см. изображение с выводом EXPLAIN ANALYZE]
loops=3 для Seq ScanWorkers Launched: 2 (лидирующий процесс + 2 воркера = всего 3 процесса)Partial HashAggregate выполняется в каждом воркеререзультаты объединяются через
Finalize HashAggregate в лидирующем процессеНесмотря на высокую скорость выполнения, это не лучший вариант для OLTP-базы данных.
При высокой конкурентной нагрузке пул воркеров становится узким местом. Запрос может работать быстро, пока ресурсов хватает, но заметно замедляться при конкуренции за CPU. Запросы с нестабильным временем выполнения — хорошие кандидаты для оптимизации.
Параллельные воркеры не заменяют здоровую архитектуру базы данных:
Добавляйте индексы, чтобы избежать полного сканирования таблиц и дорогостоящих сортировок.
Используйте summary-таблицы или materialized view, чтобы не выполнять тяжёлые агрегации на больших объёмах данных.
Разбивайте time-series таблицы на партиции, чтобы уменьшить объём данных, который приходится сканировать в типовых запросах.
👉 @SQLPortal
