19.7. Планирование запросов #
19.7.1. Конфигурация методов планировщика #
Эти параметры конфигурации дают возможность грубо влиять на планы, выбираемые оптимизатором запросов. Если автоматически выбранный оптимизатором план конкретного запроса оказался неоптимальным, в качестве временного решения можно воспользоваться одним из этих параметров и вынудить планировщик выбрать другой план. Улучшить качество планов, выбираемых планировщиком, можно и более подходящими способами, в частности, скорректировать константы стоимости (см. Подраздел 19.7.2), выполнить ANALYZE
вручную, увеличить значение параметра конфигурации default_statistics_target и увеличить объём статистики, собираемой для отдельных столбцов, воспользовавшись командой ALTER TABLE SET STATISTICS
.
enable_async_append
(boolean
) #Включает или отключает использование планировщиком планов с асинхронным добавлением данных. По умолчанию имеет значение
on
(вкл.).enable_bitmapscan
(boolean
) #Включает или отключает использование планов сканирования по битовой карте. По умолчанию имеет значение
on
(вкл.).enable_gathermerge
(boolean
) #Включает или отключает использование планов соединения посредством сбора. По умолчанию имеет значение
on
(вкл.).enable_group_by_reordering
(boolean
) #Определяет, будет ли планировщик запросов создавать план, в котором ключи
GROUP BY
отсортированы в порядке ключей дочернего узла, например в порядке сканирования индекса. Если этот параметр отключён, планировщик запросов создаст план, в котором ключиGROUP BY
отсортированы в соответствии с предложениямиORDER BY
при его наличии. Когда этот параметр включён, планировщик пытается создать более эффективный план. Значение по умолчанию —on
.enable_hashagg
(boolean
) #Включает или отключает использование планов агрегирования по хешу. По умолчанию имеет значение
on
(вкл.).enable_hashjoin
(boolean
) #Включает или отключает использование планов соединения по хешу. По умолчанию имеет значение
on
(вкл.).enable_incremental_sort
(boolean
) #Включает или отключает использование планировщиком инкрементальной сортировки. По умолчанию имеет значение
on
(вкл.).enable_indexscan
(boolean
) #Включает или отключает использование планов сканирования индекса и сканирования только индекса. По умолчанию имеет значение
on
(вкл.). См. также enable_indexonlyscan.enable_indexonlyscan
(boolean
) #Включает или отключает использование планов сканирования только индекса (см. Раздел 11.9). По умолчанию имеет значение
on
(вкл.). Чтобы использовать планы сканирования только индекса, также должен быть включён параметр enable_indexscan.enable_material
(boolean
) #Включает или отключает использование материализации при планировании запросов. Полностью исключить материализацию невозможно, но при выключении этого параметра планировщик не будет вставлять узлы материализации, за исключением случаев, где они требуются для правильности. По умолчанию этот параметр имеет значение
on
(вкл.).enable_memoize
(boolean
) #Включает или отключает использование планировщиком планов с мемоизацией для кеширования результатов параметризованного сканирования внутри соединений вложенным циклом. Этот тип плана позволяет пропускать сканирование нижележащих планов, когда результаты, соответствующие текущим параметрам, уже находятся в кеше. Менее востребованные результаты могут быть удалены из кеша, когда требуется больше места для новых записей. По умолчанию имеет значение
on
(вкл.).enable_mergejoin
(boolean
) #Включает или отключает использование планов соединения слиянием. По умолчанию имеет значение
on
(вкл.).enable_nestloop
(boolean
) #Включает или отключает использование планировщиком планов соединения с вложенными циклами. Полностью исключить вложенные циклы невозможно, но при выключении этого параметра планировщик не будет использовать данный метод, если можно применить другие. По умолчанию этот параметр имеет значение
on
.enable_parallel_append
(boolean
) #Включает или отключает использование планировщиком планов с распараллеливанием добавления данных. По умолчанию имеет значение
on
(вкл.).enable_parallel_hash
(boolean
) #Включает или отключает использование планировщиком планов соединения по хешу с распараллеливанием хеширования. Не действует, если планы соединения по хешу отключены. По умолчанию имеет значение
on
(вкл.).enable_partition_pruning
(boolean
) #Включает или отключает в планировщике возможность отсекать секции секционированных таблиц из планов запроса. Также влияет на возможность планировщика генерировать планы запросов, позволяющие исполнителю пропускать (игнорировать) секции при выполнении запросов. По умолчанию имеет значение
on
(вкл.). За подробностями обратитесь к Подразделу 5.12.4.enable_partitionwise_join
(boolean
) #Включает или отключает использование планировщиком соединения с учётом секционирования, что позволяет выполнять соединение секционированных таблиц путём соединения соответствующих секций. Соединение с учётом секционирования в настоящее время может применяться, только когда условия соединения включают все ключи секционирования; при этом ключи должны быть одного типа данных и дочерние секции должны соответствовать один-к-одному. Если этот параметр включён, количество узлов, использование памяти которых ограничено параметром
work_mem
, указанным в окончательном плане, может линейно увеличиваться в соответствии с количеством сканируемых секций. Это может привести к значительному увеличению общего потребления памяти во время выполнения запроса. Для планирования запросов также становится необходимо гораздо больше процессорного времени и памяти. По умолчанию этот параметр выключен (off
).enable_partitionwise_aggregate
(boolean
) #Включает или отключает использование планировщиком группировки или агрегирования с учётом секционирования, что позволяет выполнять группировку или агрегирование в секционированных таблицах по отдельности для каждой секции. Если предложение
GROUP BY
не включает ключи секционирования, на уровне секций может быть выполнено только частичное агрегирование, а затем требуется итоговая обработка. Если этот параметр включён, количество узлов, использование памяти которых ограничено параметромwork_mem
, указанным в окончательном плане, может линейно увеличиваться в соответствии с количеством сканируемых секций. Это может привести к значительному увеличению общего потребления памяти во время выполнения запроса. Для планирования запросов также становится необходимо гораздо больше процессорного времени и памяти. По умолчанию этот параметр выключен (off
).enable_presorted_aggregate
(boolean
) #Определяет, будет ли планировщик запросов создавать план, в котором строки предварительно отсортированы в порядке, необходимом для агрегатных функций запроса с предложениями
ORDER BY
/DISTINCT
. Если этот параметр отключён, планировщик запросов будет создавать план, который всегда будет требовать от исполнителя выполнять сортировку перед вызовом каждой агрегатной функции с предложениямиORDER BY
илиDISTINCT
. Когда этот параметр включён, планировщик пытается создать более эффективный план, предоставляющий входные данные для агрегатных функций, которые предварительно отсортированы в том порядке, который они требуют для агрегирования. Значение по умолчанию —on
.enable_seqscan
(boolean
) #Включает или отключает использование планировщиком планов последовательного сканирования. Полностью исключить последовательное сканирование невозможно, но при выключении этого параметра планировщик не будет использовать данный метод, если можно применить другие. По умолчанию этот параметр имеет значение
on
.enable_sort
(boolean
) #Включает или отключает использование планировщиком шагов с явной сортировкой. Полностью исключить явную сортировку невозможно, но при выключении этого параметра планировщик не будет использовать данный метод, если можно применить другие. По умолчанию этот параметр имеет значение
on
.enable_tidscan
(boolean
) #Включает или отключает использование планов сканирования TID. По умолчанию имеет значение
on
(вкл.).
19.7.2. Константы стоимости для планировщика #
Переменные стоимости, описанные в данном разделе, задаются по произвольной шкале. Значение имеют только их отношения, поэтому умножение или деление всех переменных на один коэффициент никак не повлияет на выбор планировщика. По умолчанию эти переменные определяются относительно стоимости чтения последовательной страницы: то есть, переменную seq_page_cost
удобно задать равной 1.0
, а все другие переменные стоимости определить относительно неё. Но при желании можно использовать и другую шкалу, например, выразить в миллисекундах фактическое время выполнения запросов на конкретной машине.
Примечание
К сожалению, какого-либо чётко определённого способа определения идеальных значений стоимости не существует. Лучше всего выбирать их как средние показатели при выполнении целого ряда разнообразных запросов, которые будет обрабатывать конкретная СУБД. Это значит, что менять их по результатам всего нескольких экспериментов очень рискованно.
seq_page_cost
(floating point
) #Задаёт приблизительную стоимость чтения одной страницы с диска, которое выполняется в серии последовательных чтений. Значение по умолчанию равно 1.0. Это значение можно переопределить для таблиц и индексов в определённом табличном пространстве, установив одноимённый параметр табличного пространства (см. ALTER TABLESPACE).
random_page_cost
(floating point
) #Задаёт приблизительную стоимость чтения одной произвольной страницы с диска. Значение по умолчанию равно 4.0. Это значение можно переопределить для таблиц и индексов в определённом табличном пространстве, установив одноимённый параметр табличного пространства (см. ALTER TABLESPACE).
При уменьшении этого значения по отношению к
seq_page_cost
система начинает предпочитать сканирование по индексу; при увеличении такое сканирование становится более дорогостоящим. Оба эти значения также можно увеличить или уменьшить одновременно, чтобы изменить стоимость операций ввода/вывода по отношению к стоимости процессорных операций, которая определяется следующими параметрами.Произвольный доступ к механическому дисковому хранилищу обычно гораздо дороже последовательного доступа, более чем в четыре раза. Однако по умолчанию выбран небольшой коэффициент (4.0), в предположении, что большой объём данных при произвольном доступе, например, при чтении индекса, окажется в кеше. Таким образом, можно считать, что значение по умолчанию моделирует ситуацию, когда произвольный доступ в 40 раз медленнее последовательного, но 90% операций произвольного чтения удовлетворяются из кеша.
Если вы считаете, что для вашей рабочей нагрузки процент попаданий не достигает 90%, вы можете увеличить параметр random_page_cost, чтобы он больше соответствовал реальной стоимости произвольного чтения. И напротив, если ваши данные могут полностью поместиться в кеше, например, когда размер базы меньше общего объёма памяти сервера, может иметь смысл уменьшить random_page_cost. С хранилищем, у которого стоимость произвольного чтения не намного выше последовательного, как например, у твердотельных накопителей, так же лучше выбрать меньшее значение random_page_cost, например
1.1
.Подсказка
Хотя система позволяет сделать
random_page_cost
меньше, чемseq_page_cost
, это лишено физического смысла. Однако сделать их равными имеет смысл, если база данных полностью кешируется в ОЗУ, так как в этом случае с обращением к страницам в произвольном порядке не связаны никакие дополнительные издержки. Кроме того, для сильно загруженной базы данных оба этих параметра следует понизить по отношению к стоимости процессорных операций, так как стоимость выборки страницы, уже находящейся в ОЗУ, оказывается намного меньше, чем обычно.cpu_tuple_cost
(floating point
) #Задаёт приблизительную стоимость обработки каждой строки при выполнении запроса. Значение по умолчанию — 0.01.
cpu_index_tuple_cost
(floating point
) #Задаёт приблизительную стоимость обработки каждой записи индекса при сканировании индекса. Значение по умолчанию — 0.005.
cpu_operator_cost
(floating point
) #Задаёт приблизительную стоимость обработки оператора или функции при выполнении запроса. Значение по умолчанию — 0.0025.
parallel_setup_cost
(floating point
) #Задаёт приблизительную стоимость запуска параллельных рабочих процессов. Значение по умолчанию — 1000.
parallel_tuple_cost
(floating point
) #Задаёт приблизительную стоимость передачи одного кортежа от параллельного рабочего процесса другому процессу. Значение по умолчанию — 0.1.
min_parallel_table_scan_size
(integer
) #Задаёт минимальный объём данных таблицы, подлежащий сканированию, при котором может применяться параллельное сканирование. Для параллельного последовательного сканирования объём сканируемых данных всегда равняется размеру таблицы, но когда используются индексы, этот объём обычно меньше. Если это значение задаётся без единиц измерения, оно считается заданным в блоках (размер которых равен
BLCKSZ
байт, обычно это 8 КБ). Значение по умолчанию — 8 мегабайт (8MB
).min_parallel_index_scan_size
(integer
) #Задаёт минимальный объём данных индекса, подлежащий сканированию, при котором может применяться параллельное сканирование. Заметьте, что при параллельном сканировании по индексу обычно не затрагивается весь индекс; здесь учитывается число страниц, которое по мнению планировщика будет затронуто при сканировании. Этот параметр также учитывается, когда нужно определить, может ли некоторый индекс обрабатываться при параллельной очистке. См. VACUUM. Если это значение задаётся без единиц измерения, оно считается заданным в блоках (размер которых равен
BLCKSZ
байт, обычно это 8 КБ). Значение по умолчанию — 512 килобайт (512kB
).effective_cache_size
(integer
) #Определяет представление планировщика об эффективном размере дискового кеша, доступном для одного запроса. Это представление влияет на оценку стоимости использования индекса; чем выше это значение, тем больше вероятность, что будет применяться сканирование по индексу, чем ниже, тем более вероятно, что будет выбрано последовательное сканирование. При установке этого параметра следует учитывать и объём разделяемых буферов PostgreSQL, и процент дискового кеша ядра, который будут занимать файлы данных PostgreSQL, хотя некоторые данные могут оказаться и там, и там. Кроме того, следует принять во внимание ожидаемое число параллельных запросов к разным таблицам, так как общий размер будет разделяться между ними. Этот параметр не влияет на размер разделяемой памяти, выделяемой PostgreSQL, и не задаёт размер резервируемого в ядре дискового кеша; он используется только в качестве ориентировочной оценки. При этом система не учитывает, что данные могут оставаться в дисковом кеше от запроса к запросу. Если это значение задаётся без единиц измерения, оно считается заданным в блоках (размер которых равен
BLCKSZ
байт, обычно это 8 КБ). Значение по умолчанию — 4 гигабайта (4GB
). ЕслиBLCKSZ
отличен от 8 КБ, значение по умолчанию корректируется пропорционально.jit_above_cost
(floating point
) #Устанавливает предел стоимости запроса, при превышении которого включается JIT-компиляция, если она поддерживается (см. Главу 30). Применение JIT занимает время при планировании, но может ускорить выполнение запроса в целом. Значение
-1
отключает JIT-компиляцию. Значение по умолчанию —100000
.jit_inline_above_cost
(floating point
) #Устанавливает предел стоимости, при превышении которого будет допускаться встраивание функций и операторов в процессе JIT-компиляции. Встраивание занимает время при планировании, но в целом может ускорить выполнение. Присваивать этому параметру значение, меньшее чем
jit_above_cost
, не имеет смысла. Значение-1
отключает встраивание. Значение по умолчанию —500000
.jit_optimize_above_cost
(floating point
) #Устанавливает предел стоимости, при превышении которого в JIT-компилированных программах может применяться дорогостоящая оптимизация. Такая оптимизация увеличивает время планирования, но в целом может ускорить выполнение. Присваивать этому параметру значение, меньшее чем
jit_above_cost
, не имеет смысла, а при значениях, превышающихjit_inline_above_cost
, положительный эффект маловероятен. Значение-1
отключает дорогостоящие оптимизации. Значение по умолчанию —500000
.
19.7.3. Генетический оптимизатор запросов #
Генетический оптимизатор запросов (GEnetic Query Optimizer, GEQO) осуществляет планирование запросов, применяя эвристический поиск. Это позволяет сократить время планирования для сложных запросов (в которых соединяются множество отношений), ценой того, что иногда полученные планы уступают по качеству планам, выбираемым при полном переборе. За дополнительными сведениями обратитесь к Главе 60.
geqo
(boolean
) #Включает или отключает генетическую оптимизацию запросов. По умолчанию она включена. В производственной среде её лучше не отключать; более гибко управлять GEQO можно с помощью переменной
geqo_threshold
.geqo_threshold
(integer
) #Задаёт минимальное число элементов во
FROM
, при котором для планирования запроса будет привлечён генетический оптимизатор. (Заметьте, что конструкцияFULL OUTER JOIN
считается одним элементом спискаFROM
.) Значение по умолчанию — 12. Для более простых запросов часто лучше использовать обычный планировщик, производящий полный перебор, но для запросов со множеством таблиц полный перебор займёт слишком много времени, чаще гораздо больше, чем будет потеряно из-за выбора не самого эффективного плана. Таким образом, ограничение по размеру запроса даёт удобную возможность управлять GEQO.geqo_effort
(integer
) #Управляет выбором между сокращением временем планирования и повышением качества плана запроса в GEQO. Это значение должна задаваться целым числом от 1 до 10. Значение по умолчанию равно пяти. Чем больше значение этого параметра, тем больше времени будет потрачено на планирование запроса, но и тем больше вероятность, что будет выбран эффективный план.
Параметр
geqo_effort
сам по себе ничего не делает, он используется только для вычисления значений по умолчанию для других переменных, влияющих на поведение GEQO (они описаны ниже). При желании эти переменные можно просто установить вручную.geqo_pool_size
(integer
) #Задаёт размер пула для алгоритма GEQO, то есть число особей в генетической популяции. Это число должно быть не меньше двух, но полезные значения обычно лежат в интервале от 100 до 1000. Если оно равно нулю (это значение по умолчанию), то подходящее число выбирается, исходя из значения
geqo_effort
и числа таблиц в запросе.geqo_generations
(integer
) #Задаёт число поколений для GEQO, то есть число итераций этого алгоритма. Оно должно быть не меньше единицы, но полезные значения находятся в том же диапазоне, что и размер пула. Если оно равно нулю (это значение по умолчанию), то подходящее число выбирается, исходя из
geqo_pool_size
.geqo_selection_bias
(floating point
) #Задаёт интенсивность селекции для GEQO, то есть селективное давление в популяции. Допустимые значения лежат в диапазоне от 1.50 до 2.00 (это значение по умолчанию).
geqo_seed
(floating point
) #Задаёт начальное значение для генератора случайных чисел, который применяется в GEQO для выбора случайных путей в пространстве поиска порядка соединений. Может иметь значение от нуля (по умолчанию) до одного. При изменении этого значения меняется набор анализируемых путей, в результате чего может быть найден как более, так и менее оптимальный путь.
19.7.4. Другие параметры планировщика #
default_statistics_target
(integer
) #Устанавливает значение ориентира статистики по умолчанию, распространяющееся на столбцы, для которых командой
ALTER TABLE SET STATISTICS
не заданы отдельные ограничения. Чем больше установленное значение, тем больше времени требуется для выполненияANALYZE
, но тем выше может быть качество оценок планировщика. Значение этого параметра по умолчанию — 100. За дополнительными сведениями об использовании статистики планировщиком запросов PostgreSQL обратитесь к Разделу 14.2.constraint_exclusion
(enum
) #Управляет использованием планировщиком ограничений таблицы для оптимизации запросов. Допустимые значения
constraint_exclusion
:on
(задействовать ограничения всех таблиц),off
(никогда не задействовать ограничения) иpartition
(задействовать ограничения только для дочерних таблиц и подзапросовUNION ALL
). Значение по умолчанию —partition
. Оно часто помогает увеличить производительность, когда применяются традиционные деревья наследования.Когда данный параметр разрешает это для таблицы, планировщик сравнивает условия запроса с ограничениями
CHECK
данной таблицы и не сканирует её, если они оказываются несовместимыми. Например:CREATE TABLE parent(key integer, ...); CREATE TABLE child1000(check (key between 1000 and 1999)) INHERITS(parent); CREATE TABLE child2000(check (key between 2000 and 2999)) INHERITS(parent); ... SELECT * FROM parent WHERE key = 2400;
Если включено исключение по ограничению, команда
SELECT
не будет сканировать таблицуchild1000
, в результате чего запрос выполнится быстрее.В настоящее время исключение по ограничению разрешено по умолчанию только в условиях, возникающих при реализации секционирования через деревья наследования. Включение этой возможности для всех таблиц влечёт дополнительные издержки на планирование, довольно заметные для простых запросов, но может не дать никакого выигрыша. Если вы не применяете секционирование через деревья наследования, имеет смысл её полностью отключить. (Заметьте, что похожая функциональность для секционированных таблиц управляется отдельным параметром, enable_partition_pruning.)
За дополнительными сведениями о применении исключений по ограничению для секционирования таблиц обратитесь к Подразделу 5.12.5.
cursor_tuple_fraction
(floating point
) #Задаёт для планировщика оценку процента строк, которые будут получены через курсор. Значение по умолчанию — 0.1 (10%). При меньших значениях планировщик будет склонен использовать для курсоров планы с «быстрым стартом», позволяющие получать первые несколько строк очень быстро, хотя для выборки всех строк может уйти больше времени. При больших значениях планировщик стремится оптимизировать общее время запроса. При максимальном значении, равном 1.0, работа с курсорами планируется так же, как и обычные запросы — минимизируется только общее время, а не время получения первых строк.
from_collapse_limit
(integer
) #Задаёт максимальное число элементов в списке
FROM
, до которого планировщик будет объединять вложенные запросы с внешним запросом. При меньших значениях сокращается время планирования, но план запроса может стать менее эффективным. По умолчанию это значение равно восьми. За дополнительными сведениями обратитесь к Разделу 14.3.Если это значение сделать равным geqo_threshold или больше, при таком объединении запросов может включиться планировщик GEQO и в результате будет получен неоптимальный план. См. Подраздел 19.7.3.
jit
(boolean
) #Определяет, может ли PostgreSQL использовать компиляцию JIT, если она поддерживается (см. Главу 30). По умолчанию он включён (
on
).join_collapse_limit
(integer
) #Задаёт максимальное количество элементов в списке
FROM
, до достижения которого планировщик будет сносить в него явные конструкцииJOIN
(за исключениемFULL JOIN
). При меньших значениях сокращается время планирования, но план запроса может стать менее эффективным.По умолчанию эта переменная имеет то же значение, что и
from_collapse_limit
, и это приемлемо в большинстве случаев. При значении, равном 1, предложенияJOIN
переставляться не будут, так что явно заданный в запросе порядок соединений определит фактический порядок, в котором будут соединяться отношения. Так как планировщик не всегда выбирает оптимальный порядок соединений, опытные пользователи могут временно задать для этой переменной значение 1, а затем явно определить желаемый порядок. За дополнительными сведениями обратитесь к Разделу 14.3.Если это значение сделать равным geqo_threshold или больше, при таком объединении запросов может включиться планировщик GEQO и в результате будет получен неоптимальный план. См. Подраздел 19.7.3.
plan_cache_mode
(enum
) #Подготовленные операторы (они могут быть подготовлены явно либо неявно, как например в PL/pgSQL) могут выполняться с использованием специализированных или общих планов. Специализированные планы строятся заново для каждого выполнения с конкретным набором значений параметров, тогда как общий план не зависит от значений параметров и может использоваться многократно. Таким образом, общий план позволяет сэкономить время планирования, но он может быть неэффективным, если идеальные планы в большой степени определяются значениями параметров. Выбор между этими вариантами обычно производится автоматически, но его можно переопределить, воспользовавшись параметром
plan_cache_mode
. Он может принимать значениеauto
(по умолчанию),force_custom_plan
(принудительно использовать специализированные планы) иforce_generic_plan
(принудительно использовать общие планы). Значение этого параметра учитывается при выполнении плана, а не при построении. За дополнительными сведениями обратитесь к PREPARE.recursive_worktable_factor
(floating point
) #Задаёт оценку планировщиком среднего размера рабочей таблицы рекурсивного запроса как множитель ожидаемого размера начальной нерекурсивной части запроса. Это помогает планировщику выбрать наиболее подходящий метод для соединения этой рабочей таблицы с другими таблицами запроса. Значение по умолчанию —
10.0
. Меньшее значение, например1.0
, может оказаться эффективнее, когда рекурсия слабо «разветвляется» от одного шага к другому, как, например, в запросах кратчайшего пути. При аналитической обработке графов оптимальные значения могут быть больше, чем значение по умолчанию.