- Настройка параметров PostgreSQL для оптимизации производительности
- Настраиваемые параметры PostgreSQL
- wal_buffers
- effective_cache_size
- work_mem
- maintenance_work_mem
- synchronous_commit
- checkpoint_timeout, checkpoint_completion_target
- Тюнинг производительности запросов в PostgreSQL
- Поиск медленных запросов
- pg_stats_statements
- auto_explain
- Тюнинг индексов
- Отсутствующие индексы
- Неиспользуемые индексы
- Примечание о статистике сред разработки
- Понимание планов выполнения
- EXPLAIN
- Стоимость
- Фактическое время
- Узнать больше о планах выполнения
- Настройка запросов
- Заметка о кеше данных и издержках
Настройка параметров PostgreSQL для оптимизации производительности
По умолчанию конфигурация PostgreSQL не настроена для рабочей нагрузки. Значения по умолчанию установлены для обеспечения работоспособности PostgreSQL везде с наименьшим количеством ресурсов. Имеются настройки по умолчанию для всех параметров базы данных. Главной обязанностью администратора базы данных или разработчика является настройка PostgreSQL в соответствии с нагрузкой их системы. В этом блоге мы изложим основные рекомендации по настройке параметров базы данных PostgreSQL для повышения производительности базы данных в соответствии с рабочей нагрузкой.
Имейте в виду, что, хотя оптимизация конфигурации сервера PostgreSQL повышает производительность, разработчик базы данных также должен быть внимательным при написании запросов. Если запросы выполняют полное сканирование таблицы, где можно использовать индекс, или выполнют тяжелые объединения или дорогостоящие операции агрегирования, тогда система все равно может работать плохо, даже если параметры базы данных настроены корректно. При написании запросов к базе данных важно обращать внимание на производительность.
Тем не менее, параметры базы данных тоже очень важны, поэтому давайте посмотрим на восемь, которые имеют наибольший потенциал для повышения производительности
Настраиваемые параметры PostgreSQL
PostgreSQL использует свой собственный буфер, а также использует буферизованный IO ядра. Это означает, что данные хранятся в памяти дважды, сначала в буфере PostgreSQL, а затем в буфере ядра. В отличие от других баз данных, PostgreSQL не обеспечивает прямой ввод-вывод. Это называется двойной буферизацией. Буфер PostgreSQL называется shared_buffer, который является наиболее эффективным настраиваемым параметром для большинства операционных систем. Этот параметр устанавливает, сколько выделенной памяти будет использоваться PostgreSQL для кеширования.
Значение по умолчанию для shared_buffer установлено очень низким, и вы не получите большой выгоды от него. Сделано это потому, что некоторые машины и операционные системы не поддерживают более высокие значения. Но в большинстве современных машин вам необходимо увеличить это значение для оптимальной производительности.
Рекомендуемое значение составляет 25% от общего объема оперативной памяти компьютера. Вам следует попробовать некоторые более низкие и более высокие значения, потому что в некоторых случаях можно получить хорошую производительность с настройкой более 25%. Но реальная конфигурация зависит от вашей машины и рабочего набора данных. Если ваш рабочий набор данных может легко поместиться в вашу оперативную память, вы можете увеличить значение shared_buffer, чтобы оно содержало всю вашу базу данных и чтобы весь рабочий набор данных мог находиться в кеше. Тем не менее, вы, очевидно, не хотите резервировать всю оперативную память для PostgreSQL.
Замечено, что в производственных средах большое значение для shared_buffer действительно дает хорошую производительность, хотя для достижения правильного баланса всегда следует проводить тесты.
Проверка значения shared_buffer
Примечание: Будьте осторожны, так как некоторые ядра не поддерживают большее значение, особенно в Windows.
wal_buffers
PostgreSQL сначала записывает записи в WAL (журнал предзаписи) в буферы, а затем эти буферы сбрасываются на диск. Размер буфера по умолчанию, определенный wal_buffers, составляет 16 МБ. Но если у вас много одновременных подключений, то более высокое значение может повысить производительность.
effective_cache_size
effective_cache_size предоставляет оценку памяти, доступной для кэширования диска. Это всего лишь ориентир, а не точный объем выделенной памяти или кеша. Он не выделяет фактическую память, но сообщает оптимизатору объем кеша, доступный в ядре. Если значение этого параметра установлено слишком низким, планировщик запросов может принять решение не использовать некоторые индексы, даже если они будут полезны. Поэтому установка большого значения всегда имеет смысл.
work_mem
Эта настройка используется для сложной сортировки. Если вам нужно выполнить сложную сортировку, увеличьте значение work_mem для получения хороших результатов. Сортировка в памяти происходит намного быстрее, чем сортировка данных на диске. Установка очень высокого значения может стать причиной узкого места в памяти для вашей среды, поскольку этот параметр относится к операции сортировки пользователя. Поэтому, если у вас много пользователей, пытающихся выполнить операции сортировки, тогда система выделит:
для всех пользователей. Установка этого параметра глобально может привести к очень высокому использованию памяти. Поэтому настоятельно рекомендуется изменить его на уровне сеанса.
Первоначальный узел сортировки запроса оценивается в 514431,86. Стоимость — это произвольная вычисляемая единица. Для приведенного выше запроса у нас work_mem всего 2 МБ. В целях тестирования давайте увеличим это значение до 256 МБ и посмотрим, повлияет ли это на стоимость.
Стоимость запроса снижена с 514431,86 до 360617,36, то есть уменьшилась на 30%.
maintenance_work_mem
maintenance_work_mem — это параметр памяти, используемый для задач обслуживания. Значение по умолчанию составляет 64 МБ. Установка большого значения помогает в таких задачах, как VACUUM, RESTORE, CREATE INDEX, ADD FOREIGN KEY и ALTER TABLE.
Время создания индекса составляет 170091,371 мс, если для параметра maintenance_work_mem установлено значение только 10 МБ, но оно уменьшается до 111274,903 мс, когда мы увеличиваем значение параметра maintenance_work_mem до 256 МБ.
synchronous_commit
Используется для обеспечения того, что фиксация транзакции будет ожидать записи WAL на диск, прежде чем вернуть клиенту статус успешного завершения. Это компромисс между производительностью и надежностью. Если ваше приложение разработано таким образом, что производительность важнее надежности, отключите synchronous_commit. В этом случае транзакция фиксируется очень быстро, потому что она не будет ожидать сброса файла WAL, но надежность будет поставлена под угрозу. В случае сбоя сервера данные могут быть потеряны, даже если клиент получил сообщение об успешном завершении фиксации транзакции.
checkpoint_timeout, checkpoint_completion_target
PostgreSQL записывает изменения в WAL. Процесс контрольной точки сбрасывает данные в файлы. Это действие выполняется, когда возникает контрольная точка (CHECKPOINT). Это дорогостоящая операция и может вызвать огромное количество операций IO. Весь этот процесс включает в себя дорогостоящие операции чтения/записи на диск. Пользователи могут всегда запустить задание контрольной точки (CHECKPOINT), когда это необходимо, или автоматизировать запуск с помощью параметров checkpoint_timeout и checkpoint_completion_target.
Параметр checkpoint_timeout используется для установки времени между контрольными точками WAL. Установка слишком низкого значения уменьшает время восстановления после сбоя, поскольку на диск записывается больше данных, но это также снижает производительность, поскольку каждая контрольная точка в конечном итоге потребляет ценные системные ресурсы.
checkpoint_completion_target — это доля времени между контрольными точками для завершения контрольной точки. Высокая частота контрольных точек может повлиять на производительность. Для плавного выполнения задания контрольной точки, checkpoint_timeout должен иметь низкое значение. В противном случае ОС будет накапливать все грязные страницы до тех пор, пока соотношение не будет соблюдено, а затем производить большой сброс.
Источник
Тюнинг производительности запросов в PostgreSQL
Настройка производительности базы данных — разработчики обычно либо любят это, либо ненавидят. Я получаю удовольствие от этого и хочу поделиться некоторыми методами, которые я использовал в последнее время для настройки плохо выполняющихся запросов в PostgreSQL. Мои методы не является исчерпывающими, скорее учебником для тех, кто просто тащится от тюнинга.
Поиск медленных запросов
Первый очевидный способ начать тюнинг — это найти конкретные операторы, которые работают плохо.
pg_stats_statements
Модуль pg_stats_statements — отличное место для начала. Он просто отслеживает статистику выполнения операторов SQL и может быть простым способом поиска неэффективных запросов.
Как только вы установили этот модуль, системное представление с именем pg_stat_statements будет доступно со всеми своими свойствами. Как только у него будет возможность собрать достаточный объем данных, ищите запросы, которые имеют относительно высокое значение total_time. Сначала сфокусируйтесь на этих операторах.
user_id | dbid | queryid | query | calls | total_time |
---|---|---|---|---|---|
16384 | 16385 | 2948 | SELECT address_1 FROM addresses a INNER JOIN people p ON a.person_id = p.id WHERE a.state = @state_abbrev; | 39483 | 15224.670 |
16384 | 16385 | 924 | SELECT person_id FROM people WHERE name = name; | 26483 | 12225.670 |
16384 | 16385 | 395 | SELECT _ FROM orders WHERE EXISTS (select _ from products where is_featured = true) | 18583 | 224.67 |
auto_explain
Модуль auto_explain также полезен для поиска медленных запросов, но имеет 2 явных преимущества: он регистрирует фактический план выполнения и поддерживает запись вложенных операторов с помощью опции log_nested_statements. Вложенные операторы — это те операторы, которые выполняются внутри функции. Если ваше приложение использует много функций, auto_explain неоценим для получения подробных планов выполнения.
Опция log_min_duration контролирует, какие планы выполнения запросов регистрируются, основываясь на том, как долго они выполняются. Например, если вы установите значение 1000, все записи, которые выполняются дольше 1 секунды, будут зарегистрированы.
Тюнинг индексов
Другой важной стратегией настройки является обеспечение правильного использования индексов. В качестве предварительного условия нам нужно включить Cборщик Cтатистики (Statistics Collector).
Postgres Statistics Collector — это подсистема первого класса, которая собирает все виды полезной статистики производительности.
Включив этот сборщик, вы получите тонны представлений pg_stat_. , которые содержат все свойства. В частности, я обнаружил, что это особенно полезно для поиска отсутствующих и неиспользуемых индексов.
Отсутствующие индексы
Отсутствующие индексы может быть одним из самых простых решений для повышения производительности запросов. Однако они не являются серебряной пулей и должны использоваться правильно (подробнее об этом позже). Если у вас включен сборщик статистики, вы можете выполнить следующий запрос (источник).
Запрос находит таблицы, в которых было больше последовательных сканирований (Sequential Scans), чем индексных сканирований (Index Scans) — явный признак того, что индекс поможет. Это не скажет вам, по каким столбцам создать индекс, так что потребуется немного больше работы. Однако, знание, какие таблицы нуждаются в них, это хороший первый шаг.
Неиспользуемые индексы
Индексируйте все сущности, правильно? Знаете ли вы, что неиспользуемые индексы могут негативно повлиять на производительность записи? Причина в том, что при создании индекса Postgres обременен задачей обновления этого индекса после операций записи (INSERT / UPDATE / DELETE). Таким образом, добавление индекса является уравновешивающим действием, поскольку оно может ускорить чтение данных (если оно создано правильно), но замедлит операции записи. Чтобы найти неиспользуемые индексы, вы можете выполнить следующий запрос.
Примечание о статистике сред разработки
Полагаться на статистику, полученную из локальной базы данных разработки, может быть проблематично. В идеале вы можете получить приведенную выше статистику с вашей рабочей машины или сгенерировать ее из восстановленной рабочей резервной копии. Зачем? Факторы окружения могут изменить работу оптимизатора запросов Postgres. Два примера:
- когда у машины меньше памяти, PostgreSQL может быть не в состоянии выполнить Hash Join, в противном случае он сможет и сделает это быстрее.
- если в таблице не так много строк (как в базе данных разработки), PostgresSQL может предпочесть выполнять последовательное сканирование таблицы, а не использовать доступный индекс. Когда размеры таблиц невелики, Seq Scan может быть быстрее. (Примечание: вы можете запустить в сеансе, чтобы оптимизатор предпочел использовать индексы, даже если последовательное сканирование может быть быстрее. Это полезно при работе с базами данных разработки, в которых нет большого количества данных)
Понимание планов выполнения
Теперь, когда вы нашли несколько медленных запросов, самое время начать веселье.
EXPLAIN
Команда EXPLAIN, безусловно, обязательна при настройке запросов. Он говорит вам, что на самом деле происходит. Чтобы использовать его, просто добавьте к запросу EXPLAIN и запустите его. PostgreSQL покажет вам план выполнения, который он использовал.
При использовании EXPLAIN для настройки, я рекомендую всегда использовать опцию ANALYZE (EXPLAIN ANALYZE), поскольку она дает вам более точные результаты. Опция ANALYZE фактически выполняет оператор (а не просто оценивает его), а затем объясняет его.
Давайте окунемся и начнем понимать вывод EXPLAIN. Вот пример:
Первое, что нужно понять, это то, что каждый блок с отступом с предшествующим «->» (вместе с верхней строкой) называется узлом. Узел — это логическая единица работы («шаг», если хотите) со связанной стоимостью и временем выполнения. Стоимость и время, представленные на каждом узле, являются совокупными и сводят все дочерние узлы. Это означает, что самая верхняя строка (узел) показывает совокупную стоимость и фактическое время для всего оператора. Это важно, потому что вы можете легко детализировать для определения, какие узлы являются узким местом.
Стоимость
Первое число — это начальные затраты (затраты на получение первой записи), а второе число — это затраты на обработку всего узла (общие затраты от начала до конца).
Фактически, это стоимость, которую, по оценкам PostgreSQL, придется выполнить для выполнения оператора. Это число не означает сколько времени потребуется для выполения запроса, хотя обычно существует прямая зависимость, необходимого для выполнения. Стоимость — это комбинация из 5 рабочих компонентов, используемых для оценки требуемой работы: последовательная выборка, непоследовательная (случайная) выборка, обработка строки, оператор (функция) обработки и запись индекса обработки. Стоимость представляет собой операции ввода-вывода и загрузки процессора, и здесь важно знать, что относительно высокая стоимость означает, что PostgresSQL считает, что ему придется выполнять больше работы. Оптимизатор принимает решение о том, какой план выполнения использовать, исходя из стоимости. Оптимизатор предпочитает более низкие затраты.
Фактическое время
В миллисекундах первое число — это время запуска (время для извлечения первой записи), а второе число — это время, необходимое для обработки всего узла (общее время от начала до конца). Легко понять, верно?
В приведенном выше примере потребовалось 55,009 мс для получения первой записи и 55,012 мс для завершения всего узла.
Узнать больше о планах выполнения
Есть несколько действительно хороших статей для понимания результатов EXPLAIN. Вместо того, чтобы пытаться пересказать их здесь, я рекомендую потратить время на то, чтобы по-настоящему понять их, перейдя к этим 2 замечательным ресурсам:
Настройка запросов
Теперь, когда вы знаете, какие операторы работают плохо и можете видеть свои планы выполнения, пришло время приступить к настройке запроса для повышения производительности. Здесь вы вносите изменения в запросы и/или добавляете индексы, чтобы попытаться получить лучший план выполнения. Начните с узких мест и посмотрите, есть ли какие-то изменения, которые вы можете сделать, чтобы сократить расходы и/или время выполнения.
Заметка о кеше данных и издержках
При внесении изменений и оценке планов выполнения, чтобы увидеть будут ли улучшения, важно знать, что последующие выполнения могут зависеть от кэширования данных, которые дают представление о лучших результатах. Если вы запустите запрос один раз, сделаете исправление и запустите его второй раз, скорее всего, он будет выполняться намного быстрее, даже если план выполнения не будет более благоприятным. Это связано с тем, что PostgreSQL мог кэшировать данные, используемые при первом запуске, и может использовать их при втором запуске. Поэтому вы должны выполнять запросы как минимум 3 раза и усреднять результаты, чтобы сравнить издержки.
Вещи, которые я узнал, могут помочь улучшить планы выполнения:
- Индексы
- Исключите последовательное сканирование (Seq Scan), добавив индексы (если размер таблицы не мал)
- При использовании многоколоночного индекса убедитесь, что вы обращаете внимание на порядок, в котором вы определяете включенные столбцы — Дополнительная информация
- Попробуйте использовать индексы, которые очень избирательны к часто используемым данным. Это сделает их использование более эффективным.
- Условие ГДЕ
- Избегайте LIKE
- Избегайте вызовов функций в условии WHERE
- Избегайте больших условий IN()
- JOINы
- При объединении таблиц попробуйте использовать простое выражение равенства в предложении ON (т.е. a.id = b.person_id). Это позволяет использовать более эффективные методы объединения (т. Е. Hash Join, а не Nested Loop Join)
- Преобразуйте подзапросы в операторы JOIN, когда это возможно, поскольку это обычно позволяет оптимизатору понять цель и, возможно, выбрать лучший план.
- Правильно используйте СОЕДИНЕНИЯ: используете ли вы GROUP BY или DISTINCT только потому, что получаете дублирующиеся результаты? Это обычно указывает на неправильное использование JOIN и может привести к более высоким затратам
- Если план выполнения использует Hash Join, он может быть очень медленным, если оценки размера таблицы неверны. Поэтому убедитесь, что статистика вашей таблицы точна, пересмотрев стратегию очистки (vacuuming strategy )
- По возможности избегайте коррелированных подзапросов; они могут значительно увеличить стоимость запроса
- Используйте EXISTS при проверке существования строк на основе критерия, поскольку он подобен короткому замыканию (останавливает обработку, когда находит хотя бы одно совпадение)
- Общие рекомендации
- Делайте больше с меньшими затратами; Процессор быстрее чем операции ввода/вывода (I/O)
- Используйте Common Table Expressions и временные таблицы, когда вам нужно выполнить цепочечные запросы.
- Избегайте операторов LOOP и предпочитайте операции SET
- Избегайте COUNT (*), поскольку PostgresSQL для этого выполняет сканирование таблиц (только для версий
Источник