Исходя из опыта MySQL, где производительность хранимых процедур (старая статья) и удобство использования сомнительны, я оцениваю PostgreSQL для нового продукта для моей компании.
Одна из вещей, которые я хотел бы сделать, - это перенести некоторую логику приложения в хранимые процедуры, поэтому я здесь прошу сделать DO и DON'Ts (лучшие практики) при использовании функций в PostgreSQL (9.0), особенно в отношении ошибок производительности.
postgresql
best-practices
plpgsql
Дерек Дауни
источник
источник
Ответы:
Строго говоря, термин «хранимые процедуры» указывает на процедуры 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.
источник
Некоторые делают:
источник
Вообще говоря, перемещение логики приложения в базу данных будет означать, что оно быстрее - в конце концов, оно будет работать ближе к данным.
Я считаю (но не уверен на 100%), что функции языка SQL быстрее, чем те, которые используют любые другие языки, потому что они не требуют переключения контекста. Недостатком является то, что процедурная логика не допускается.
PL / pgSQL является наиболее развитой и полной функциональностью из встроенных языков, но для производительности можно использовать C (хотя он только выиграет от вычислительно-интенсивных функций)
источник
Вы можете сделать некоторые очень интересные вещи, используя пользовательские функции (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, чтобы не перегружать ваши процессоры.
источник