Идемпотентность и волатильность функций в Greenplum и PostgreSQL

Идемпотентность и волатильность функций в Greenplum и PostgreSQL

Что такое волатильные функции, зачем они нужны и чем опасны: разбираем на примере Greenplum и PostgreSQL. К чему приведет некорректное использование атрибутов изменчивости в SQL-запросе или UDF-функции распределенной MPP-СУБД.

Что такое волатильность функции и почему это важно для Greenplum

Волатильной или изменчивой считается функция, значение которой может изменяться даже в пределах одного сканирования таблицы, поэтому ее вызовы не могут быть оптимизированы. Таким образом, волатильная функция неидемпотентна. Идемпотентность предполагает, что повторное применение функции к результату её же применения не изменяет конечный результат. Волатильная функция, напротив, может возвращать разные результаты при каждом вызове, даже если её аргументы остаются неизменными. Поэтому она не соответствует свойству идемпотентности. В Greenplum и PostgreSQL волатильность функций определяет, как часто функция может возвращать разные результаты при одинаковых входных параметрах. Это влияет на оптимизацию запросов. Существует 3 уровня волатильности функций:

  • IMMUTABLE — функции, которые всегда возвращают один и тот же результат при одинаковых входных параметрах. Такая функция основана только на информации, непосредственно находящуюся в списке аргументов. При одинаковых значениях аргументов она всегда возвращает одинаковый результат. Например, функция, которая вычисляет математическую операцию без использования внешних данных. Оптимизатор может безопасно кэшировать результаты таких функций.
  • STABLE — функции, которые гарантируют одинаковый результат в пределах одного запроса, но могут возвращать разные результаты в разных запросах. Такие функции могут обращаться к внешним данным, которые не изменяются в ходе выполнения одного запроса. В рамках одного сканирования таблицы такая функция возвращает тот же результат для тех же значений аргументов, но результаты меняются в зависимости от оператора SQL. Например, функции, которые считывают данные из таблиц, которые не изменяются в процессе одного выполнения запроса.
  • VOLATILE — функции, которые могут возвращать разные результаты даже в рамках одного вызова. Это функции, которые зависят от состояния системы, времени или других внешних факторов. Например, функции, которые возвращают текущее время или случайное значение. Значения такой функции могут изменяться в рамках одного сканирования таблицы.

Функция, определенная с атрибутом IMMUTABLE, может быть запущена во время планирования запроса, тогда как волатильная функция с атрибутом VOLATILE должна быть запущена для каждой строки в запросе.

Хотя функции STABLE и IMMUTABLE не являются идемпотентными, между ними есть относительно небольшая разница для простых интерактивных запросов, которые планируются и немедленно запускаются. Когда функция запускается один раз во время планирования или один раз во время запуска выполнения запроса, разница незаметна. Но она очень существенна при сохранении плана выполнения запроса и его повторном использовании. Если функция неправильно промаркирована атрибутом IMMUTABLE, Greenplum может преждевременно свернуть ее в константу во время планирования, повторно используя устаревшее значение во время последующего выполнения плана. На практике это особенно вероятно при использовании выражений PREPARED или выражений на PL/pgSQL, которые кэшируют планы запросов.

По умолчанию пользовательские функции в Greenplum считаются волатильными, т.е. объявляются как VOLATILE. Поэтому разработчику надо явно указывать IMMUTABLE или STABLE для своих UDF-функций. Функция, которая запускает запросы для доступа к таблицам, поддерживается только тогда, когда она выполняется на экземпляре координатора. Кроме этого, функция может запускать SELECT-команды, которые обращаются только к реплицированным таблицам на экземплярах сегмента. Функция, которая обращается к распределенным по хэшу или случайно распределенным таблицам, должна быть определена с атрибутом EXECUTE ON COORDINATOR. Иначе она может возвращать неверные результаты при использовании в сложном запросе. Без атрибута EXECUTE ON оптимизация планировщика может перенести вызов функции на экземпляры сегмента, что не всегда корректно.

Все оконные функции в Greenplum являются неизменяемыми, как и встроенные расширенные агрегатные функции MEDIAN(expr), sum(array[]), pivot_sum(label[], label, expr), unnest(array[]). Для наилучших результатов оптимизации, функции следует назначать самую строгую характеристику изменчивости, которой она соответствует. Любая функция с побочными эффектами должна быть помечена как VOLATILE, чтобы обращения к ней не исключались при оптимизации.

Волатильность функции, написанных на SQL или на любом другом стандартном процедурном языке, определяет видимость изменений, вызванных этой функцией. Функция VOLATILE будет видеть такие изменения, а STABLE и IMMUTABLE — нет. Это поведение реализуется посредством снимков в MVCC: STABLE и IMMUTABLE используют снимок, полученный в начале вызывающего запроса, тогда как функции VOLATILE получают свежий снимок в начале каждого запроса, который они выполняют. Функции STABLE и IMMUTABLE не замечает изменений в базе данных, произведённых ими, поскольку они не проявляются в моментальном снимке данных. Поэтому PostgreSQL требует, чтобы функции STABLE и IMMUTABLE не содержали никаких SQL-команд, кроме SELECT, чтобы избежать модификации данных.

Таким образом, некорректное использование атрибутов волатильности в SQL-запросе UDF-функции Greenplum может привести к неправильным результатам запросов или снижению производительности. Например, если функция, которая изменяет данные, будет помечена как IMMUTABLE, оптимизатор может закэшировать ее результаты, что приведет к некорректным данным. Поэтому важно правильно определять волатильность функций, чтобы избежать потенциальных ошибок и обеспечить корректную работу Greenplum и PostgreSQL. Кроме того, для Greenplum также важно корректно задавать атрибут места выполнения функции EXECUTE ON, чтобы оптимизировать использование ресурсов и обеспечивает корректное выполнение запросов. Подробнее об этом мы поговорим далее.

Изменчивые функции в распределенном кластере MPP-СУБД

В Greenplum волатильность функции указывает, как она запускается, а выражение EXECUTE ON задает, где она запускается. Например, функция с атрибутом EXECUTE ON COORDINATOR запускается только на экземпляре координатора, а функция с атрибутом EXECUTE ON ALL SEGMENTS запускается на всех экземплярах первичного сегмента, но не на координаторе. Если определяемая пользователем функция запускает запросы к таблицам, надо указать для этой UDF-функции EXECUTE ON COORDINATOR. По умолчанию используется атрибут EXECUTE ON ANY, который указывает, что функция может быть запущена на координаторе или любом экземпляре сегмента, и она возвращает тот же результат независимо от того, где она запущена. Каждая UDF-функция также по умолчанию объявляется как EXECUTE ON ANY, и Greenplum сама определяет, где ее выполнить. Атрибут EXECUTE ON INITPLAN указывает, что функция содержит команду SQL, которая отправляет запросы экземплярам сегмента и требует специальной обработки на экземпляре координатора Greenplum, когда это возможно.

Чтобы обеспечить согласованность данных, можно безопасно использовать функции VOLATILE и STABLE в операторах, которые оцениваются и запускаются из координатора. Например, оператор выборки, т.е. SELECT-команда без FROM-предложения запускается на координаторе. Если в SELECT-запросе есть предложение FROM, содержащее распределенную таблицу, и функция в это предложении возвращает набор строк, оператор выборки может выполняться на сегментах.

Поскольку данные в Greenplum разделены по сегментам, каждый из которых представляет собой отдельную базу данных PostgreSQL, то для предотвращения несогласованных или неожиданных результатов, не рекомендуется запускать волатильные функции на уровне сегмента, если они содержат команды SQL или как-то меняют базу данных. Например, такие функции, как setval() не разрешено запускать на распределенных данных в Greenplum, поскольку они могут привести к несогласованности данных между экземплярами сегмента. Функция может выполнять запросы только для чтения к реплицированным таблицам DISTRIBUTED REPLICATED в сегментах, но любая команда SQL, которая изменяет данные, должна выполняться на экземпляре координатора.

Если функция выполняется на мастер-хосте Greenplum, а не на сегментах, это может привести к значительным накладным расходам на передачу данных между сегментами и мастером, что замедлит выполнение запроса. Поэтому, если функция должна обрабатывать данные, которые уже распределены по сегментам, её лучше выполнять на сегментах. Кроме того, некорректное задание атрибута EXECUTE ON может привести к логическим ошибкам. Например, если функция должна выполняться на каждом сегменте отдельно, чтобы корректно обработать локальные данные, её выполнение только на мастер-хосте может дать неверные результаты.

Наконец, правильное распределение выполнения функций помогает сбалансировать нагрузку между узлами кластера MPP-СУБД, повышая общую производительность распределенной системы.

Освойте администрирование и эксплуатацию Greenplum для аналитики больших данных на специализированных курсах в нашем лицензированном учебном центре обучения и повышения квалификации для разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве:

Статья:

Курсы:

Наш сайт:

Копирование, размножение, распространение, перепечатка (целиком или частично), или иное использование материала допускается только с письменного разрешения правообладателя ООО "УЦ Коммерсант"

Начать дискуссию