Производительность функции

46

Исходя из опыта MySQL, где производительность хранимых процедур (старая статья) и удобство использования сомнительны, я оцениваю PostgreSQL для нового продукта для моей компании.

Одна из вещей, которые я хотел бы сделать, - это перенести некоторую логику приложения в хранимые процедуры, поэтому я здесь прошу сделать DO и DON'Ts (лучшие практики) при использовании функций в PostgreSQL (9.0), особенно в отношении ошибок производительности.

Дерек Дауни
источник
Вы имеете в виду, что вы не хотите, чтобы ответы упоминали что-либо, не связанное с производительностью?
Джек Дуглас
Крис Траверс много блогов о преимуществах использования хранимых процедур, например, здесь: ledgersmbdev.blogspot.de/2012/07/… а здесь: ledgersmbdev.blogspot.de/2012/07/… просто пролистать свой блог, есть много интересных статей на эту тему.
a_horse_with_no_name

Ответы:

51

Строго говоря, термин «хранимые процедуры» указывает на процедуры SQL в Postgres, введенные в Postgres 11.

Есть также функции , выполняющие почти, но не совсем то же самое, и они были там с самого начала.

Функции с LANGUAGE sql, в основном, представляют собой просто пакетные файлы с простыми командами SQL в оболочке функций (и, следовательно, атомарные, всегда выполняемые внутри одной транзакции), принимающие параметры. Все операторы в функции SQL планируются сразу , что несколько отличается от выполнения одного оператора за другим и может влиять на порядок, в котором принимаются блокировки.

Для чего-то большего, самый зрелый язык - это PL / pgSQL ( LANGUAGE plpgsql). Он хорошо работает и улучшается с каждым выпуском за последнее десятилетие, но лучше всего служит связующим звеном для команд SQL. Он не предназначен для тяжелых вычислений (кроме команд SQL).

Функции PL / pgSQL выполняют запросы как подготовленные операторы . Повторное использование кэшированных планов запросов сокращает некоторые затраты на планирование и делает их немного быстрее, чем эквивалентные операторы SQL, что может быть заметным эффектом в зависимости от обстоятельств. У этого могут также быть побочные эффекты как в этом связанном вопросе:

Это несет в себе преимущества и недостатки подготовленных заявлений - как описано в руководстве . Для запросов к таблицам с нерегулярным распределением данных и изменяющимися параметрами динамический SQL с EXECUTEможет работать лучше, когда выгода от оптимизированного плана выполнения для данного параметра (параметров) превышает стоимость перепланирования.

Начиная с Postgres 9.2 общие планы выполнения все еще кэшируются для сеанса, но, цитируя руководство :

Это происходит немедленно для подготовленных операторов без параметров; в противном случае это происходит только после того, как пять или более исполнений произведут планы, чьи средние сметные расходы (включая накладные расходы на планирование) стоят дороже, чем сметная стоимость общего плана.

Мы получаем лучшее из обоих миров большую часть времени (за исключением некоторых дополнительных затрат) без использования (ab) EXECUTE. Подробности в Что нового в PostgreSQL 9.2 из PostgreSQL Wiki .

Postgres 12 вводит дополнительную переменную сервераplan_cache_mode для принудительного использования общих или пользовательских планов. Для особых случаев используйте с осторожностью.

Вы можете выиграть с помощью функций на стороне сервера, которые предотвращают дополнительные обращения к серверу базы данных из вашего приложения. Пусть сервер выполнит как можно больше за один раз и вернет только четко определенный результат.

Избегайте вложения сложных функций, особенно табличных функций ( RETURNING SETOF recordили TABLE (...)). Функции - это черные ящики, которые представляют собой барьеры оптимизации для планировщика запросов. Они оптимизируются отдельно, а не в контексте внешнего запроса, что упрощает планирование, но может привести к неудачным планам. Кроме того, стоимость и размер результата функций не могут быть надежно предсказаны.

Исключение из этого правила являются простые функции SQL ( LANGUAGE sql), которые могут быть «встраиваются» - если какие - то предварительные условия . Узнайте больше о том, как планировщик запросов работает в этой презентации Нила Конвея (продвинутый материал).

В PostgreSQL функция всегда автоматически запускается внутри одной транзакции . Все это удается или ничего. Если возникает исключение, все откатывается. Но есть обработка ошибок ...

Вот почему функции не совсем «хранимые процедуры» (хотя этот термин используется иногда, вводит в заблуждение). Некоторые команды, например VACUUM, CREATE INDEX CONCURRENTLYили CREATE DATABASEне могут выполняться внутри блока транзакции, поэтому они не разрешены в функциях. (Пока нет ни в процедурах SQL, ни в Postgres 11. Это может быть добавлено позже.)

За эти годы я написал тысячи функций plpgsql.

Эрвин Брандштеттер
источник
2
@nhahtdh: «автоматическая транзакция» не является техническим термином. Это был просто изящный способ сказать ... что он говорит сейчас после моего разъяснения. Не автономная транзакция вообще. «Автономный», случается, похожее слово.
Эрвин Брандштеттер
4
Ваши ответы скомпилированы отсюда, и SO может быть эпическим руководством по передовой практике PostGreSQL.
Давос
10

Некоторые делают:

  • По возможности используйте SQL в качестве языка функций, так как PG может встроить операторы
  • Используйте IMMUTABLE / STABLE / VOLATILE правильно, так как PG может кэшировать результаты, если они неизменны или стабильны
  • Используйте STRICT правильно, так как PG может просто возвращать ноль, если какой-либо ввод равен нулю, вместо запуска функции
  • Рассмотрим PL / V8, когда вы не можете использовать SQL в качестве языка функций. Это быстрее, чем PL / pgSQL в некоторых ненаучных тестах, которые я запускал
  • Используйте LISTEN / NOTIFY для более длительных процессов, которые могут произойти вне транзакции
  • Рассмотрите возможность использования функций для реализации нумерации страниц, так как пагинация на основе ключей может выполняться быстрее, чем пагинация на основе LIMIT.
  • Убедитесь, что вы тестируете свои функции
Нил Макгиган
источник
Впервые я вижу утверждение, что PL / V8 быстрее, чем PL / pgSQL. Есть ли у вас (опубликованные) цифры, подтверждающие это?
a_horse_with_no_name
@a_horse_with_no_name нет, не знаю. Как я уже сказал, я провел несколько ненаучных тестов. Они были в основном логические, а не доступ к данным. Я постараюсь сделать несколько повторяющихся тестов на Рождество и опубликовать здесь.
Нил Макгиган
@a_horse_with_no_name Вот простой пример для FizzBuzz plv8 против plpgsql: blog.databasepatterns.com/2014/08/plv8-vs-plpgsql.html
Нил Макгиган
8

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

Я считаю (но не уверен на 100%), что функции языка SQL быстрее, чем те, которые используют любые другие языки, потому что они не требуют переключения контекста. Недостатком является то, что процедурная логика не допускается.

PL / pgSQL является наиболее развитой и полной функциональностью из встроенных языков, но для производительности можно использовать C (хотя он только выиграет от вычислительно-интенсивных функций)

Джек Дуглас
источник
7

Вы можете сделать некоторые очень интересные вещи, используя пользовательские функции (UDF) в postgresql. Например, есть десятки возможных языков, которые вы можете использовать. Встроенные pl / sql и pl / pgsql способны и надежны и используют метод песочницы, чтобы пользователи не делали слишком опасных действий. UDF, написанные на C, дают вам максимальную мощность и производительность, поскольку они работают в том же контексте, что и сама база данных. Тем не менее, это все равно что играть с огнем, потому что даже небольшие ошибки могут вызвать огромные проблемы, со сбоем бэкэндов или повреждением данных. Обычные языки pl, такие как pl / R, pl / ruby, pl / perl и т. Д., Дают вам возможность писать слои базы данных и приложения на одних и тех же языках. Это может быть удобно, так как это означает, что вам не нужно учить программиста Perl java, pl / pgsql и т. Д. Писать UDF.

Наконец, есть язык pl / proxy . Этот язык UDF позволяет вам запускать ваше приложение на десятках или более внутренних серверов postgresql для масштабирования. Он был разработан хорошими людьми в Skype и в основном позволяет горизонтальное масштабирование для бедного человека. На удивление легко писать.

Теперь что касается вопроса производительности. Это серая зона. Вы пишете приложение для одного человека? Или за 1000? или за 10 000 000? То, как вы создадите свое приложение и будете использовать UDF, будет зависеть от того, как вы пытаетесь масштабироваться. Если вы пишете для тысяч и тысяч пользователей, то главное, что вы хотите сделать, - максимально снизить нагрузку на БД. Пользовательские функции, которые уменьшают объем данных, перемещаемых и возвращаемых в базу данных, помогут снизить нагрузку ввода-вывода. Однако, если они начинают увеличивать нагрузку на процессор, они могут стать проблемой. Вообще говоря, снижение нагрузки ввода-вывода является первым приоритетом, а следующий - обеспечение эффективности UDF, чтобы не перегружать ваши процессоры.

Скотт Марлоу
источник