Управление spill-файлами
Spill-файлы (или work-файлы) — это временные файлы, которые Greengage DB создает на диске, когда запросам требуется больше памяти, чем им выделено для выполнения. Они хранят промежуточные результаты соединений, сортировок, группировок и других операций, выходящих за пределы доступной памяти.
Spill-файлы помогают избежать ошибок out-of-memory (OOM) при выполнении SQL-запросов. Вместо того чтобы прервать выполнение запроса при нехватке памяти, Greengage DB переносит избыточные временные данные на диск и продолжает выполнение. Это повышает стабильность системы, особенно при одновременном исполнении большого количества запросов или при обработке большого объема данных одним запросом.
Однако использование spill-файлов может негативно повлиять на производительность кластера, так как операции чтения и записи на диск медленнее, чем работа с памятью. Кроме того, spill-файлы временно занимают дисковое пространство, особенно при выполнении запросов с соединениями больших таблиц, сортировкой или агрегацией больших объемов данных.
Spill-файлы являются временными. Greengage DB автоматически удаляет их после завершения запроса или транзакции. Обычно администраторам не требуется управлять этими файлами вручную. При этом важно понимать, когда и почему создаются spill-файлы — это полезно для настройки запросов, оптимизации конфигурации и контроля за использованием ресурсов.
Когда появляются spill-файлы
Spill-файлы создаются, когда запросу требуется больше памяти, чем ему выделено. Обычно это происходит при ресурсоемких операциях, обрабатывающих большие объемы данных или использующих алгоритмы с высокой нагрузкой на память.
Типичные причины появления spill-файлов:
-
Недостаточное выделение памяти в конфигурации кластера. Объем памяти, доступный запросу, задается параметрами конфигурации, такими как
statement_mem,max_statement_memиwork_mem. Убедитесь, что их значения соответствуют предполагаемой нагрузке и ресурсам кластера. -
Перекос данных в кластере. Если данные распределены неравномерно, некоторые сегменты могут хранить значительно больше данных, чем другие. Это может привести к нехватке памяти на отдельных сегментах, даже если общая нагрузка сбалансирована.
-
Неоптимальные запросы, например:
-
Соединения больших таблиц, особенно хеш-соединения, которые используют хеш-таблицы в памяти. Если объем данных превышает лимит памяти, части хеш-таблицы или входных данных записываются на диск.
-
Агрегации без фильтров (выражения
WHERE), в которых обрабатывается весь набор данных без предварительной выборки. -
Выражение
DISTINCTна большом числе полей. Такие запросы генерируют все уникальные комбинации значений указанных столбцов, что требует дополнительного места для хранения. -
Выражение
ORDER BYна больших выборках. Когда используетсяORDER BY, Greengage DB нужно место для хранения результата сортировки. Сначала они размещаются в памяти, но при недостатке памяти записываются на диск.
-
-
Устаревшая статистика. Если статистика таблиц отсутствует или неактуальна, оптимизатор может неверно оценить объем данных, что приведет к созданию spill-файлов.
Понимание этих причин помогает администраторам и разработчикам оптимизировать запросы и конфигурацию системы, чтобы избежать использования spill-файлов.
Настройка использования spill-файлов
Greengage DB позволяет настраивать следующие параметры работы со spill-файлами:
-
расположение
-
лимиты
-
сжатие
Расположение
Расположение spill-файлов Greengage DB определяется назначенными табличными пространствами. Spill-файлы создаются в каталоге pgsql_tmp/ в указанном табличном пространстве. Например, при использовании табличного пространства по умолчанию путь к spill-файлам имеет вид $MASTER_DATA_DIRECTORY/base/pgsql_tmp/ .
Чтобы поменять расположение spill-файлов, используйте следующие параметры конфигурации:
-
temp_tablespaces— задает общие табличные пространства для всех временных объектов в базе данных, включая spill-файлы. -
temp_spill_files_tablespaces— указывает отдельные табличные пространства специально для spill-файлов. Если этот параметр задан, он имеет приоритет надtemp_tablespacesдля размещения spill-файлов.
Чтобы настроить отдельное табличное пространство для spill-файлов, создайте его и выполните следующие команды:
$ gpconfig -c temp_spill_files_tablespaces -v '<spill_tablespace>' --masteronly
$ gpstop -u
где <spill_tablespace> — имя отдельного табличного пространства.
Вы также можете указать несколько табличных пространств через запятую:
$ gpconfig -c temp_spill_files_tablespaces -v '<spill_tablespace1>,...,<spill_tablespaceN>' --masteronly
$ gpstop -u
Подробности смотрите в разделе Настройка табличных пространств по умолчанию.
Лимиты
Максимальное количество одновременно существующих spill-файлов задается параметром gp_workfile_limit_files_per_query.
Он определяет, сколько spill-файлов может создать один запрос на каждом сегменте.
Значение по умолчанию — 100000 — достаточно для большинства случаев.
Если этого лимита недостаточно для выполнения запросов, рекомендуется оптимизировать их, чтобы уменьшить использование spill-файлов.
Значение 0 отключает лимит.
Для управления объемом дискового пространства, используемого под spill-файлы, доступны следующие параметры:
-
gp_workfile_limit_per_query— максимальный объем (в килобайтах), который один запрос может использовать на каждом сегменте. -
gp_workfile_limit_per_segment— общий объем, доступный для spill-файлов всех выполняемых запросов на каждом сегменте.
По умолчанию оба параметра равны 0, что означает отсутствие ограничений.
Чтобы задать лимиты на использование диска spill-файлами, выполните команды следующего вида:
$ gpconfig -c gp_workfile_limit_per_query -v '2097152'
$ gpconfig -c gp_workfile_limit_per_segment -v '16777216'
$ gpstop -r
Обратите внимание, что gp_workfile_limit_per_segment задается на сегментах и требует перезапуска кластера.
Параметр gp_workfile_limit_per_query можно изменить с помощью перезагрузки конфигурации.
Порог создания spill-файлов
При использовании ресурсных групп для управления ресурсами кластера можно настроить порог использования памяти (в процентах). Когда транзакция потребляет этот процент доступной памяти группы, создаются spill-файлы.
Порог использования памяти можно задать на двух уровнях:
-
Ресурсная группа — атрибут ресурсной группы
MEMORY_SPILL_RATIOзадает порог по умолчанию для всех транзакций в этой группе. -
Сессия — параметр конфигурации
memory_spill_ratioпозволяет переопределить значение по умолчанию для конкретной сессии или транзакции.
Подробнее о пороге создания spill-файлов см. в разделе Создание spill-файлов: MEMORY_SPILL_RATIO.
Сжатие
Чтобы уменьшить размер spill-файлов, можно включить их сжатие с помощью параметра gp_workfile_compression типа boolean:
$ gpconfig -c gp_workfile_compression -v ON --masteronly
$ gpstop -u
При использовании сжатия учитывайте, что оно снижает нагрузку на диск и объем хранения за счет увеличения нагрузки на CPU.
Сжатие применяется к spill-файлам, которые порождаются операциями хеш-агрегирования (hash aggregate) и хеш-соединения (hash join). Остальные spill-файлы не сжимаются.
Сжатие использует дополнительную память, которая не учитывается в лимите statement_mem.
Объем этой памяти ограничен параметром gp_workfile_compression_overhead_limit, который по умолчанию равен 2GB.
Если одновременно выполняется много запросов со сжатием spill-файлов и достигается этот лимит, Greengage DB перестает сжимать новые spill-файлы.
Чтобы изменить лимит дополнительной памяти для сжатия, задайте новое значение gp_workfile_compression_overhead_limit или установите его в 0, чтобы отключить ограничение.
Проверка spill-файлов
Spill-файлы являются временными — они существуют только во время выполнения запроса. Чтобы определить, создает ли запрос spill-файлы, необходимо просматривать соответствующие представления системного каталога во время выполнения запроса. После завершения запроса файлы и связанные с ними статистики автоматически удаляются.
Существует два способа проверить использование spill-файлов:
-
Отслеживать представления системного каталога
gp_workfile_*во время выполнения запроса. Эти представления отображают информацию об активных spill-файлах в момент просмотра. -
Использовать SQL-команду
EXPLAIN ANALYZE. Ее вывод содержит сведения о spill-файлах, созданных при выполнении запроса. Это позволяет оценить объем данных, сброшенных на диск, и выявить возможные проблемы в плане выполнения запроса.
Чтобы воспроизвести примеры из этого раздела, выполните следующие шаги:
-
Подключитесь к мастер-хосту Greengage DB с помощью
psql, как описано в статье Подключение к Greengage DB с использованием psql. -
Создайте новую базу данных и подключитесь к ней:
CREATE DATABASE marketplace; \c marketplace -
Создайте тестовую таблицу:
CREATE TABLE items ( item_id SERIAL PRIMARY KEY, category TEXT, price NUMERIC, count NUMERIC, value NUMERIC, description TEXT ) DISTRIBUTED BY (item_id); -
Заполните таблицу большим объемом данных, например, одним миллионом строк:
INSERT INTO items (category, price, count, value, description) SELECT CASE WHEN id % 5 = 0 THEN 'Category A' WHEN id % 5 = 1 THEN 'Category B' WHEN id % 5 = 2 THEN 'Category C' WHEN id % 5 = 3 THEN 'Category D' ELSE 'Category E' END, RANDOM() * 1000, RANDOM() * 1000, RANDOM() * 1000, md5(RANDOM()::text) FROM generate_series(1, 1000000) id; -
Установите низкий лимит памяти для выполнения запроса, который будет исчерпан при выполнении ресурсоемкого запроса, например,
5MB. При достижении этого лимита будут создаваться spill-файлы:SET statement_mem = '5MB';
В такой конфигурации следующий SQL-запрос использует всю доступную память и начнет создавать spill-файлы:
SELECT DISTINCT (item_id, price, count), value
FROM items
GROUP BY item_id
ORDER BY value;
Чтобы проверить spill-файлы, создаваемые этим запросом, откройте другое подключение psql к той же базе данных и выполните запросы к системным представлениям, перечисленным ниже во время выполнения запроса.
После завершения запроса spill-файлы удаляются, и представления не содержат данных.
Не запускайте приведенные примеры в production-средах. Они меняют конфигурацию и могут создавать высокую нагрузку, что потенциально приведет к сбоям. Используйте для тестирования только тестовые стенды или стенды разработки.
Представления gp_toolkit.gp_workfile_*
Следующие представления административной схемы gp_toolkit содержат информацию об активных spill-файлах.
Они полезны для мониторинга и оптимизации использования ресурсов:
-
gp_toolkit.gp_workfile_usage_per_query— показывает использование spill-файлов по запросам. Содержит агрегированную информацию о количестве и размере spill-файлов, созданных каждым запросом. Представление содержит такие метаданные, как размер файлов, их количество, соответствующий SQL-запрос и другую информацию. Это позволяет определить запросы с высоким потреблением ресурсов.Пример:
SELECT datname, usename, query, segid, size, numfiles FROM gp_toolkit.gp_workfile_usage_per_query;Пример вывода:
datname | usename | query | segid | size | numfiles -------------+---------+--------------------------------------------------------------------------------------------+-------+----------+---------- marketplace | gpadmin | SELECT DISTINCT (item_id, price, count), value FROM items GROUP BY item_id ORDER BY value; | 0 | 18317312 | 1 marketplace | gpadmin | SELECT DISTINCT (item_id, price, count), value FROM items GROUP BY item_id ORDER BY value; | 3 | 18317312 | 1 marketplace | gpadmin | SELECT DISTINCT (item_id, price, count), value FROM items GROUP BY item_id ORDER BY value; | 1 | 18219008 | 1 marketplace | gpadmin | SELECT DISTINCT (item_id, price, count), value FROM items GROUP BY item_id ORDER BY value; | 2 | 18350080 | 1 (4 rows)
-
gp_toolkit.gp_workfile_entries— предоставляет подробную информацию об активных spill-файлах в кластере. В отличие от представленияgp_workfile_usage_per_query, которое агрегирует данные на уровне запроса, вgp_toolkit.gp_workfile_entriesкаждая строка соответствует одному оператору (столбецoptype), создавшему spill-файл. Такой уровень детализации помогает при глубоком анализе запросов и их настройке.Пример:
SELECT datname, usename, query, segid, optype, size, numfiles FROM gp_toolkit.gp_workfile_entries;Пример вывода:
datname | usename | query | segid | optype | size | numfiles -------------+---------+--------------------------------------------------------------------------------------------+-------+--------+----------+---------- marketplace | gpadmin | SELECT DISTINCT (item_id, price, count), value FROM items GROUP BY item_id ORDER BY value; | 2 | Sort | 18382848 | 1 marketplace | gpadmin | SELECT DISTINCT (item_id, price, count), value FROM items GROUP BY item_id ORDER BY value; | 3 | Sort | 18284544 | 1 marketplace | gpadmin | SELECT DISTINCT (item_id, price, count), value FROM items GROUP BY item_id ORDER BY value; | 0 | Sort | 18350080 | 1 marketplace | gpadmin | SELECT DISTINCT (item_id, price, count), value FROM items GROUP BY item_id ORDER BY value; | 1 | Sort | 18284544 | 1 (4 rows)
-
gp_toolkit.gp_workfile_usage_per_segment— отображает использование spill-файлов на уровне сегментов. Позволяет оценить, как spill-файлы распределяются по кластеру. Это представление помогает выявить дисбаланс нагрузки и возможные узкие места в производительности.Пример:
SELECT * FROM gp_toolkit.gp_workfile_usage_per_segment;Пример вывода:
segid | size | numfiles
-------+----------+----------
1 | 18251776 | 1
0 | 18350080 | 1
2 | 18350080 | 1
3 | 18284544 | 1
-1 | 0 |
(5 rows)
Представления gp_toolkit.gp_workfile_entries, gp_toolkit.gp_workfile_usage_per_query и gp_toolkit.gp_workfile_usage_per_segment доступны всем пользователям.
Однако для обычных пользователей (не суперпользователей) отображаются только данные по базам, к которым у них есть доступ.
Команда EXPLAIN ANALYZE
В тестовых средах вы можете проверить, создает ли запрос spill-файлы, с помощью команды EXPLAIN ANALYZE.
Эта команда строит план запроса и выполняет его, а затем выводит подробную статистику выполнения, включая информацию об использовании памяти и наличии spill-файлов.
Например, следующая команда выводит план запроса и сведения о spill-файлах:
EXPLAIN ANALYZE
SELECT DISTINCT (item_id, price, count), value
FROM items
GROUP BY item_id
ORDER BY value;
Результат:
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------
-------
Unique (cost=2665.04..2875.04 rows=16800 width=68) (actual time=1630.148..4123.738 rows=1000000 loops=1)
Group Key: value, (ROW(item_id, price, count))
-> Gather Motion 4:1 (slice2; segments: 4) (cost=2665.04..2791.04 rows=16800 width=68) (actual time=1630.147..3624.408 rows=1000000 loops=1)
Merge Key: value, (ROW(item_id, price, count))
-> Unique (cost=2665.04..2791.04 rows=4200 width=68) (actual time=1613.354..1982.876 rows=250461 loops=1)
Group Key: value, (ROW(item_id, price, count))
-> Sort (cost=2665.04..2707.04 rows=4200 width=68) (actual time=1613.352..1884.099 rows=250461 loops=1)
Sort Key (Distinct): value, (ROW(item_id, price, count))
Sort Method: external merge Disk: 71488kB
-> Redistribute Motion 4:4 (slice1; segments: 4) (cost=478.00..1486.00 rows=4200 width=68) (actual time=287.676..560.936 rows=250461 lo
ops=1)
Hash Key: value
-> HashAggregate (cost=478.00..814.00 rows=4200 width=68) (actual time=286.894..509.883 rows=250659 loops=1)
Group Key: item_id
Extra Text: (seg1) 249548 groups total in 32 batches; 1 overflows; 249548 spill groups.
(seg1) Hash chain length 3.9 avg, 14 max, using 72165 of 73728 buckets; total 1 expansions.
-> Seq Scan on items (cost=0.00..436.00 rows=4200 width=100) (actual time=0.012..130.936 rows=250659 loops=1)
Planning time: 0.139 ms
(slice0) Executor memory: 884863K bytes.
* (slice1) Executor memory: 3671K bytes avg x 4 workers, 3671K bytes max (seg0). Work_mem: 2511K bytes max, 19201K bytes wanted.
* (slice2) Executor memory: 4753K bytes avg x 4 workers, 4753K bytes max (seg0). Work_mem: 4698K bytes max, 73909K bytes wanted.
Memory used: 5120kB
Memory wanted: 148316kB
Optimizer: Postgres query optimizer
Execution time: 4246.067 ms
(25 rows)
Если во время выполнения запроса создавались spill-файлы, это отображается в строках со звездочкой ( * (slice1) и * (slice2)) после плана запроса.
Параметры Memory used и Memory wanted показывают, сколько памяти фактически использовал запрос и сколько ему требовалось.
Обычно при создании spill-файлов значение Memory wanted превышает Memory used, которое, в свою очередь, зависит от statement_mem (лимита памяти на запрос) и memory_spill_ratio (порога использования памяти).
Минимизация использования spill-файлов
Хотя spill-файлы позволяют выполнять запросы при нехватке памяти, они могут существенно снижать производительность и занимать дисковое пространство. Поэтому по возможности рекомендуется сводить к минимуму их использование.
Всегда старайтесь уменьшить число spill-файлов, изменяя запросы, распределение данных или настройки памяти. Следуйте этим рекомендациям, чтобы снизить или полностью избежать создания spill-файлов:
-
Поддерживайте статистику в актуальном состоянии. Регулярно обновляйте статистику таблиц и индексов. Актуальная статистика помогает планировщику запросов выбирать более эффективные планы выполнения. Узнайте больше в статье Сбор статистики с помощью ANALYZE.
-
Оптимизируйте SQL-запросы. Проверьте и при необходимости упростите логику запросов для повышения производительности:
-
Используйте фильтры. Применяйте условия фильтрации как можно раньше, чтобы сократить объем обрабатываемых в дальнейшем данных.
-
Используйте правильный порядок таблиц в соединениях. При выполнении
LEFT JOINGreengage DB строит хеш-таблицу на основе таблицы справа в выражении. Если справа указана большая таблица, это может привести к нехватке памяти. По возможности размещайте большие таблицы в левой части выраженияLEFT JOIN. Также ограничивайте число возвращаемых полей, чтобы сократить объем обрабатываемых данных. -
Избегайте
ORDER BY. В общем случае использованиеORDER BYв СУБД Greengage не рекомендуется. Если сортировка необходима, старайтесь уменьшить объем данных или выполняйте сортировку на стороне клиента после возвращения результата запроса. -
Минимизируйте число полей в
DISTINCT. Сократите число полей, указанных вDISTINCT, чтобы снизить нагрузку. Каждое дополнительное поле резко увеличивает число уникальных комбинаций, что может привести к исчерпанию памяти.
-
-
Обеспечьте равномерное распределение данных. Убедитесь, что данные равномерно распределены по сегментам кластера. Это гарантирует, что все сегменты обрабатывают сопоставимые объемы данных и не перегружаются. Узнайте больше в статье Распределение данных.
-
Выделяйте достаточно ресурсов. Если предполагаемая рабочая нагрузка включает запросы, создающие spill-файлы и не поддающиеся оптимизации, выделите больше ресурсов для их эффективного выполнения:
-
Увеличьте лимиты памяти, заданные параметрами конфигурации
statement_memиmax_statement_mem. -
Настройте доступные ресурсы с помощью ресурсных групп или ресурсных очередей для более эффективного управления памятью и CPU.
-
Чтобы избежать сбоев из-за чрезмерного количества или объема spill-файлов, настройте параметры, ограничивающие их использование: gp_workfile_limit_files_per_query, gp_workfile_limit_per_query и gp_workfile_limit_per_segment.