¿Declarar la volatilidad de la función INMUTABLE puede dañar el rendimiento?

9

Las funciones de Postgres se declaran con clasificación de volatilidad VOLATILE, STABLEoIMMUTABLE . Se sabe que el proyecto es muy estricto con estas etiquetas para las funciones integradas. Y con buen motivo. Ejemplo destacado: los índices de expresión solo permiten IMMUTABLEfunciones y esas deben ser verdaderamente inmutables para evitar resultados incorrectos.

Las funciones definidas por el usuario aún pueden declararse libremente según lo elija el propietario. El manual aconseja:

Para obtener los mejores resultados de optimización, debe etiquetar sus funciones con la categoría de volatilidad más estricta que sea válida para ellas.

... y agrega una extensa lista de cosas que pueden salir mal con una etiqueta de volatilidad incorrecta.

Aún así, hay casos en los que tiene sentido falsificar la inmutabilidad. Principalmente cuando sabe que la función es, de hecho, inmutable dentro de su alcance. Ejemplo:

Dejando a un lado todas las posibles implicaciones en la integridad de los datos , ¿cuál es el efecto en el rendimiento? Se podría suponer que declarar una función IMMUTABLEsolo puede ser beneficiosa para el rendimiento . ¿Es eso así?

¿La declaración de volatilidad de la función puede IMMUTABLE dañar el rendimiento?

Supongamos que Postgres 10 actual lo reduce, pero todas las versiones recientes son de interés.

Erwin Brandstetter
fuente
1
Como nota al margen también, todo el "verdaderamente inmutable" en los índices de expresión es una verdadera pita. Esa es una IU horrible. Deberíamos poder hacerlo de FORCEcualquier manera. El 100% de los DBA PostgreSQL experimentados mienten para evitar esa interfaz de usuario con funciones de contenedor. Al menos con FORCE, no necesitaríamos envoltorios y no tendríamos que mentir sobre la volatilidad declarada de fn.
Evan Carroll
1
Supongo que FORCEse supone que los índices de expresión aceptan funciones no inmutables (al tiempo que los marcan como posibles puntos de falla). Sí, eso parecería una solución más elegante que los envoltorios de funciones inmutables.
Erwin Brandstetter
No sé casi nada sobre PostGres, pero ¿no es la volatilidad volátil redundante? Qué significa eso? En serio, no esperes que esto sea confiable, ¿porque es una locura ?
Anthony
@ Anthony: Aclaré un poco más. Siga el enlace al manual para más detalles.
Erwin Brandstetter

Respuestas:

7

Sí, puede dañar el rendimiento.

Las funciones SQL simples se pueden "en línea" en la consulta de llamada. Citando el Wiki de Postgres :

Las funciones SQL (es decir LANGUAGE SQL), bajo ciertas condiciones, tendrán sus cuerpos de función integrados en la consulta de llamada en lugar de ser invocados directamente. Esto puede tener ventajas sustanciales de rendimiento ya que el cuerpo de la función queda expuesto al planificador de la consulta de llamada, que puede aplicar optimizaciones tales como el plegado constante, el pushdown de calidad, etc.

El énfasis audaz es mío.

Para hacer cumplir la corrección, hay una serie de condiciones previas. Uno de ellos :

si se declara la función IMMUTABLE, la expresión no debe invocar ninguna función u operador no inmutable

Es decir, las funciones SQL que utilizan funciones no inmutables pero que aún se declaran IMMTUTABLEestán excluidas de esta optimización. Activado por estas respuestas relacionadas en SO, he estado realizando pruebas exhaustivas:

Básicamente, comparando estas dos variantes de una función SQL simple (asignando fechas a una integer, ignorando el año que no importa para ese propósito):

CREATE FUNCTION f_mmdd_tc_s(date) RETURNS int LANGUAGE sql STABLE    AS
$$SELECT to_char($1, 'MMDD')::int$$;

CREATE FUNCTION f_mmdd_tc_i(date) RETURNS int LANGUAGE sql IMMUTABLE AS
$$SELECT to_char($1, 'MMDD')::int$$;  -- cannot be inlined!

La función Postgres to_char()es única STABLE, no IMMUTABLE(todas las instancias sobrecargadas de la misma, por razones más allá del alcance de esta respuesta ). Entonces, el segundo es falso IMMUTABLEy resulta ser 5 veces más lento en una prueba simple:

db <> violín aquí

Este ejemplo específico se puede reemplazar con el equivalente:

CREATE FUNCTION f_mmdd(date) RETURNS int LANGUAGE sql IMMUTABLE AS
$$SELECT (EXTRACT(month FROM $1) * 100 + EXTRACT(day FROM $1))::int$$;

Sería parecer más caro con dos llamadas a funciones y más cálculos. Sin embargo, la IMMUTABLEetiqueta es verdadera (además, la función de uso es más rápido y coaccionar texta integeres más caro, también).

2 veces más rápido que la variante más rápida anterior (10 veces más rápido que el más lento). El punto es: usar IMMUTABLEfunciones cuando sea posible , para no tener que "hacer trampa".

Erwin Brandstetter
fuente
¡Geniales hallazgos! Tenga un seguimiento inmediato: dba.stackexchange.com/q/212198/2639
Evan Carroll
Sabes lo que creo que extrañé aquí, que no sabía. Eso STABLEtambién está en línea. Pensé que el optimizador solo IMMUTABLEfuncionaría en línea .
Evan Carroll
VOLATILEigual de bien.
Erwin Brandstetter
El wiki dice que la función se declara ESTABLE o INMUTABLE wiki.postgresql.org/wiki/Inlining_of_SQL_functions
Evan Carroll
.. bajo "Condiciones de alineación para funciones de tabla ". No para funciones escalares. Lo demostré en el violín: dbfiddle.uk/…
Erwin Brandstetter