Venant d'un arrière-plan MySQL, où la procédure stockée performance (article plus ancien) et tilisabilité sont discutables, j'évalue PostgreSQL pour un nouveau produit pour mon entreprise.
L'une des choses que j'aimerais faire est de déplacer une partie de la logique d'application dans des procédures stockées, donc je demande ici des choses à faire et à ne pas faire (meilleures pratiques) sur l'utilisation des fonctions dans PostgreSQL (9.0 ), notamment en ce qui concerne les pièges de performance.
À strictement parler, le terme "procédures stockées" pointe vers procédures SQL dans Postgres, introduit avec Postgres 11. Lié:
Il y a aussi fonctions, faisant presque mais pas tout à fait la même chose, et cela existe depuis le début.
Les fonctions avec LANGUAGE sql
Sont essentiellement des fichiers batch avec des commandes SQL simples dans un wrapper de fonction (et donc atomiques, toujours exécutées dans un single transaction) accepte les paramètres. Toutes les instructions dans une fonction SQL sont prévues à la fois, ce qui est subtilement différent de l'exécution d'une instruction après l'autre et peut affecter l'ordre dans lequel les verrous sont pris.
Pour quoi que ce soit de plus, le langage le plus mature est PL/pgSQL (LANGUAGE plpgsql
). Il fonctionne bien et a été amélioré avec chaque version au cours de la dernière décennie, mais il sert mieux de colle pour les commandes SQL. Il n'est pas destiné aux calculs lourds (sauf avec les commandes SQL).
Les fonctions PL/pgSQL exécutent des requêtes telles que instructions préparées . La réutilisation des plans de requête mis en cache réduit certains frais généraux de planification et les rend un peu plus rapides que les instructions SQL équivalentes, ce qui peut être un effet notable selon les circonstances. Cela peut également avoir des effets secondaires comme dans cette question connexe:
Cela comporte les avantages et les inconvénients des instructions préparées - comme indiqué dans le manuel . Pour les requêtes sur des tables avec une distribution de données irrégulière et des paramètres variables SQL dynamique avec EXECUTE
peut mieux fonctionner lorsque le le gain d'un plan d'exécution optimisé pour le ou les paramètres donnés l'emporte sur le coût de la replanification.
Depuis Postgres 9.2, les plans d'exécution génériques sont toujours mis en cache pour la session mais, en citant le manuel :
Cela se produit immédiatement pour les instructions préparées sans paramètres; sinon, cela ne se produit qu'après que cinq exécutions ou plus ont produit des plans dont le coût moyen estimé (y compris les frais généraux de planification) est plus cher que l'estimation de coût du plan générique.
Nous obtenons le meilleur des deux mondes la plupart du temps (moins des frais généraux ajoutés) sans (ab) utiliser EXECUTE
. Détails dans Quoi de neuf dans PostgreSQL 9.2 du wiki PostgreSQL .
Postgres 12 introduit la variable supplémentaire variable serveur plan_cache_mode
pour forcer les plans génériques ou personnalisés. Pour les cas particuliers, utilisez avec soin.
Vous pouvez gagner gros avec des fonctions côté serveur qui empêchent les allers-retours supplémentaires au serveur de base de données depuis votre application. Demandez au serveur d'exécuter autant que possible à la fois et de renvoyer uniquement un résultat bien défini.
Évitez d'imbriquer des fonctions complexes, en particulier les fonctions de table (RETURNING SETOF record
Ou TABLE (...)
). Les fonctions sont des boîtes noires se présentant comme des barrières d'optimisation pour le planificateur de requêtes. Ils sont optimisés séparément, pas dans le contexte de la requête externe, ce qui simplifie la planification, mais peut entraîner des plans moins que parfaits. De plus, le coût et la taille des résultats des fonctions ne peuvent pas être prédits de manière fiable.
exception à cette règle sont de simples fonctions SQL (LANGUAGE sql
), Qui peuvent être "en ligne" - si certaines conditions préalables sont remplies . En savoir plus sur le fonctionnement du planificateur de requêtes dans ce présentation de Neil Conway (trucs avancés).
Dans PostgreSQL, une fonction s'exécute toujours automatiquement à l'intérieur d'une seule transaction . Tout cela réussit ou rien. Si une exception se produit, tout est annulé. Mais il y a gestion des erreurs ...
C'est aussi pourquoi les fonctions sont pas exactement "procédures stockées" (même si ce terme est parfois utilisé, de manière trompeuse). Certaines commandes comme VACUUM
, CREATE INDEX CONCURRENTLY
ou CREATE DATABASE
ne peuvent pas s'exécuter à l'intérieur d'un bloc de transaction , ils ne sont donc pas autorisés dans les fonctions. (Ni dans les procédures SQL, pour l'instant, à partir de Postgres 11. Cela pourrait être ajouté plus tard.)
J'ai écrit des milliers de fonctions plpgsql au fil des ans.
Certains DO:
De manière générale, le déplacement de la logique d'application dans la base de données signifie qu'elle est plus rapide - après tout, elle se rapprochera des données.
Je crois (mais je ne suis pas sûr à 100%) que fonctions de langage SQL sont plus rapides que celles utilisant d'autres langues car elles ne nécessitent pas de changement de contexte. L'inconvénient est qu'aucune logique procédurale n'est autorisée.
PL/pgSQL est le plus mature et le plus complet des langages intégrés - mais pour les performances, C peut être utilisé ( bien qu'il ne bénéficiera que des fonctions intensives en calcul)
Vous pouvez faire des choses très intéressantes en utilisant des fonctions définies par l'utilisateur (UDF) dans postgresql. Par exemple, il existe des dizaines de langues possibles que vous pouvez utiliser. Les pl/sql et pl/pgsql intégrés sont à la fois capables et fiables et utilisent une méthode sandbox pour empêcher les utilisateurs de faire quelque chose de trop terriblement dangereux. Les FDU écrites en C vous offrent le summum de la puissance et des performances, car elles s'exécutent dans le même contexte que la base de données elle-même. Cependant, c'est comme jouer avec le feu, car même de petites erreurs peuvent causer d'énormes problèmes, avec des backends qui plantent ou des données corrompues. Les langages custome pl, comme pl/R, pl/Ruby, pl/Perl, etc. vous offrent la possibilité d'écrire les couches de base de données et d'application dans les mêmes langues. Cela peut être pratique, car cela signifie que vous n'avez pas à enseigner à un programmeur Perl Java ou pl/pgsql etc. pour écrire un UDF.
Enfin, il y a la langue pl/proxy . Ce langage UDF vous permet d'exécuter votre application sur des dizaines ou plus de serveurs postgresql principaux à des fins de mise à l'échelle. Il a été développé par les bons employés de Skype et permet essentiellement une solution de mise à l'échelle horizontale pour un pauvre. Il est également très facile d'écrire.
Maintenant, quant à la question des performances. Ceci est une zone grise. Ecrivez-vous une application pour une seule personne? Ou pour 1000? ou pour 10 000 000? La façon dont vous créez votre application et utilisez les FDU dépendra BEAUCOUP de la façon dont vous essayez de vous adapter. Si vous écrivez pour des milliers et des milliers d'utilisateurs, la principale chose à faire est de réduire la charge sur la base de données autant que possible. Les FDU qui réduisent la quantité de données déplacées vers l'extérieur et dans la base de données aideront à réduire la charge IO. Cependant, si elles commencent à augmenter la charge du processeur, elles peuvent être un problème à ce moment-là. IO la charge est la première priorité, et s'assurer que les UDF sont efficaces afin de ne pas surcharger vos CPU est la prochaine.