Introduction
PostgreSQL donne aux développeurs la possibilité de choisir entre deux possibilités de stockage pour les données binaires volumineuses :Bytea et LargeObjects.
Les grands objets existent depuis longtemps et PostgreSQL dispose d'un moyen intelligent de stocker de grandes données binaires. Il le fait en le divisant en morceaux de LOBLKSIZE (un quart de BLCKSZ). De cette façon, les tuples de pg_largeobject ne débordent pas sur la table toast.
D'autre part bytea stocke les données binaires directement dans le tuple, ce qui peut entraîner de mauvaises performances en fonction de l'apparence de votre schéma.
Cela sonne bien si vous disposez d'une interface intelligente pour gérer la manipulation de ces fichiers binaires, en particulier si la mise à jour ne modifie qu'une petite partie de l'ensemble du fichier binaire.
Mais normalement, nous ne prenons pas la peine d'écrire du code qui tire parti de cela, et à la place, nous écrivons à nouveau l'ensemble des données binaires.
Je pense que l'une des choses qui poussent les gens à adopter de gros objets sont les fonctions disponibles pour importer et exporter des fichiers directement du serveur de base de données vers son système de fichiers. Il y a un inconvénient à cela :si l'application se trouve sur un autre serveur, vous aurez besoin de plus de code pour déplacer le fichier à l'emplacement où il est nécessaire.
Un problème auquel vous pourriez être confronté
Ces derniers jours, j'ai dû examiner une base de données utilisée pour stocker des informations sur les sessions des utilisateurs à partir d'un système Java CAS. J'ai découvert qu'il y avait près de 100 millions d'objets volumineux dans la base de données, pas très volumineux.
J'ai parcouru les tables d'utilisateurs en vérifiant les champs qui avaient un oid champ, puis je croise les valeurs de ces champs avec le pg_largeobject_metadata table. J'ai trouvé que 96% de ces gros objets étaient orphelins. Ce sont de gros objets qui n'ont été référencés par aucun tuple des tables utilisateur.
Une enquête plus approfondie a conclu qu'Hibernate n'avait pas pris soin de purger les gros objets qu'il avait créés lors de la suppression ou de la mise à jour de tuples avec des champs oid. Il générait donc une grande quantité de ballonnements qui ne pouvaient pas être nettoyés en aspirant, mais devaient être purgés manuellement de la table pg_largeobjects.
Dans le cas particulier de la base de données CAS, cette requête a permis d'identifier les gros objets encore utilisés :
SELECT unnest(array[expiration_policy, authentication, services_granted_access_to]) FROM public.ticketgrantingticket UNION SELECT unnest(array[expiration_policy, service]) FROM public.serviceticket
La requête peut être utilisée pour exclure de la liste des objets volumineux ceux à supprimer. Quelque chose comme ça :
SELECT lo_unlink(pg_largeobject_metadata.oid) FROM pg_largeobject_metadata WHERE pg_largeobject_metadata.oid NOT IN ( SELECT unnest(array[expiration_policy, authentication, services_granted_access_to]) FROM public.ticketgrantingticket UNION SELECT unnest(array[expiration_policy, service]) FROM public.serviceticket )
Conclusion
Les objets volumineux ont leurs problèmes, tout comme les autres types de données (en particulier lors de l'utilisation de types pour stocker de grandes données binaires). Il appartient aux développeurs et aux administrateurs de base de données de tirer parti des avantages et d'atténuer les inconvénients.
Nous avons donné une requête possible pour effectuer le nettoyage, mais il existe également une belle extension qui nettoie les gros objets orphelins avec des déclencheurs :Large Object Manager
Certaines personnes peuvent préférer exécuter une requête de purge pendant les heures creuses au lieu d'exécuter un déclencheur à chaque UPDATE et SUPPRIMER . Sur les systèmes avec très, très peu de UPDATE et/ou SUPPRIMER taux, un déclencheur sur chaque table qui a un oid domaine, semble une solution plus élégante. Et toute perte de performance pour devoir exécuter la fonction de déclenchement serait superflue.
Dans tous les cas, les gros objets ont toujours de grands fans, probablement en raison des fonctions internes fournies pour importer et exporter les données binaires directement vers le système de fichiers local. Avec bytea, vous utiliseriez normalement plus de mémoire au niveau de l'application. C'est une procédure très courante de lire complètement le champ binaire dans une variable, puis de le traiter.
Je pourrais écrire quelque chose sur l'utilisation de bytea que j'ai utilisé dans l'un de mes développements passés dans un futur article de blog.