Einleitung
PostgreSQL gibt Entwicklern die Möglichkeit, zwischen zwei möglichen Speichermöglichkeiten für große Binärdaten zu wählen:Bytea und LargeObjects.
Große Objekte gibt es schon seit langer Zeit, und PostgreSQL bietet eine intelligente Möglichkeit, große Binärdaten zu speichern. Dazu wird es in Blöcke von LOBLKSIZE (ein Viertel von BLCKSZ) aufgeteilt. Auf diese Weise werden die Tupel von pg_largeobject nicht auf dem Toasttisch verschüttet.
Andererseits bytea speichert die Binärdaten direkt im Tupel, was je nach Aussehen Ihres Schemas zu schlechter Leistung führen kann.
Das hört sich großartig an, wenn Sie eine intelligente Schnittstelle haben, um mit der Manipulation dieser Binärdateien umzugehen, insbesondere wenn das Update nur einen kleinen Teil der gesamten Binärdatei ändert.
Aber normalerweise machen wir uns nicht die Mühe, Code zu schreiben, der dies ausnutzt, und schreiben stattdessen die gesamten Binärdaten erneut.
Eines der Dinge, die meiner Meinung nach Menschen dazu bringen, große Objekte zu übernehmen, sind die verfügbaren Funktionen zum Importieren und Exportieren von Dateien direkt vom Datenbankserver in sein Dateisystem. Das hat einen Nachteil:Wenn sich die Anwendung auf einem anderen Server befindet, benötigen Sie mehr Code, um die Datei an den Ort zu verschieben, an dem sie benötigt wird.
Ein Problem, mit dem Sie möglicherweise konfrontiert sind
In den vergangenen Tagen musste ich eine Datenbank untersuchen, in der Informationen zu Benutzersitzungen aus einem Java-CAS-System gespeichert wurden. Ich fand heraus, dass es fast 100 Millionen große Objekte in der Datenbank gab, nicht sehr große.
Ich ging die Benutzertabellen durch und überprüfte die Felder mit einem oid Feld, und dann vergleiche ich die Werte in diesen Feldern mit den pg_largeobject_metadata Tisch. Ich fand heraus, dass 96 % dieser großen Objekte verwaiste Objekte waren. Das sind große Objekte, die von keinem Tupel aus den Benutzertabellen referenziert wurden.
Weitere Untersuchungen ergaben, dass Hibernate sich nicht darum gekümmert hat, die großen Objekte zu löschen, die es beim Löschen oder Aktualisieren von Tupeln mit OID-Feldern erstellt hat. Es erzeugte also eine große Menge an Aufblähung, die nicht durch Staubsaugen beseitigt werden konnte, sondern manuell aus der pg_largeobjects-Tabelle entfernt werden musste.
Im speziellen Fall der CAS-Datenbank diente diese Abfrage dazu, die noch verwendeten LargeObjects zu identifizieren:
SELECT unnest(array[expiration_policy, authentication, services_granted_access_to]) FROM public.ticketgrantingticket UNION SELECT unnest(array[expiration_policy, service]) FROM public.serviceticket
Die Abfrage kann verwendet werden, um aus der Liste der großen Objekte auszuschließen, welche entfernt werden sollen. Etwa so:
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 )
Schlussfolgerung
Große Objekte haben ihre Probleme, genau wie andere Datentypen (insbesondere wenn Typen zum Speichern großer Binärdaten verwendet werden). Es liegt an den Entwicklern und Datenbankadministratoren, die Vorteile zu nutzen und die Nachteile abzumildern.
Wir haben eine mögliche Abfrage gegeben, um die Bereinigung durchzuführen, aber es gibt auch eine nette Erweiterung, die die verwaisten großen Objekte mit Triggern bereinigt:Large Object Manager
Einige Leute ziehen es vielleicht vor, eine Bereinigungsabfrage während ruhiger Stunden auszuführen, anstatt bei jedem UPDATE einen Trigger auszuführen und LÖSCHEN . Auf Systemen mit sehr, sehr niedrigem UPDATE und/oder LÖSCHEN Rate, ein Trigger für jede Tabelle, die einen oid hat Feld, scheint eine elegantere Lösung zu sein. Und jeder Performanceverlust für die Ausführung der Triggerfunktion wäre überflüssig.
Auf jeden Fall haben große Objekte immer noch große Fans, wahrscheinlich wegen der internen Funktionen, die bereitgestellt werden, um die Binärdaten direkt in das lokale Dateisystem zu importieren und zu exportieren. Mit bytea würden Sie normalerweise mehr Speicher auf der Anwendungsebene verwenden. Es ist ein sehr gängiges Verfahren, das Binärfeld komplett in eine Variable einzulesen und dann zu verarbeiten.
Vielleicht schreibe ich in einem zukünftigen Blogbeitrag etwas über die Verwendung von Bytea, das ich in einer meiner früheren Entwicklungen verwendet habe.