Neuerungen seit PostgreSQL 8.3?

HOT - Heap Only Tupels

HOT verhindert redundante Indexeiträge und gibt den Speicher von gelöschten oder nicht mehr benötigten aktualisierten Datensätzen zur Wiederverwendung frei. Anstatt für die gesamte Tabelle wird VACUUM nur für die aktuelle Speicherseite ausgeführt, dies wird auch als *Defragementierung* bezeichnet.

Bei einem UPDATE werden Datensätze in PostgreSQL nicht überschrieben, sondern es wird eine neue Version des Datensatzes eingefügt. Sind für die betreffende Tabelle Indexe definiert, werden auch dort neue Einträge eingefügt, die die neue Datensatz-Version referenzieren. So entsteht eine Versions-Historie aller aktualisierter Datensätze und gleichzeitig wachsen Tabellen und Indexe mit jedem UPDATE.

Ab PostgreSQL 8.3 ändert sich dieses Verhalten: Wenn ein UPDATE keine Spalten verändert, für die ein Index definiert ist oder die an der Definition eines partiellen oder funktionalen Index beteiligt sind, schreibt PostgreSQL 8.3 keinen neuen (redundanten, weil mit demselben Schlüssel) Indexeintrag. Nicht mehr jedem Datensatz in einer Versions-Historie ist immer genau ein Eintrag im Index zugeordnet, sondern möglicherweise einer ganzen Kette (update chain) von aktualisierten Datensätzen. Heap-Only-Tupels sind solche Datensätze, die keinen *eigenen* Eintrag im Index haben. Dies führt zu kleineren Indexen und damit zu schnelleren Suchergebnissen.

Vor PostgreSQL 8.3 war es nur mit dem Kommando VACUUM möglich, *dead HOT tuples* aus Tabellen zu bereinigen und den Speicherplatz zur Wiederverwendung freizugeben. HOT ermöglicht die Freigabe und Wiederverwendung von Speicher, allerdings nur innerhalb der aktuellen Speicherseite. Dadurch wird eine gleichmäßigere Leistung erreicht, Speicher kann in einer Tabelle früher zur Wiederverwendung freigegeben werden, die Notwendigkeit eines VACUUM verringert sich und die Gefahr, dass sich Tabellen und Indexe aufblähen, wird reduziert.

Selbstjustierender Background-Writer

PostgreSQL's Background-Writer ist ein Serverprozess mit der Aufgabe, *dirty* (veränderten) shared buffers zwischen zwei Checkpoints (zyklisch) auf die Platte zu schreiben und damit die I/O-Last des nachfolgenden Checkpoints zu verringern. Dieser Prozess wurde in PostgreSQL 8.3 optimiert.

Bis zur Version 8.2 führte das ständige Scannen und Speichern von *dirty* Blocks bei häufig aktualisierten Tabellen dazu, dass dieselben Blöcke zwischen zwei Checkpoints immer wieder gespeichert wurden, weil viele Aktualisierungen viele *dirty* Blocks erzeugten. Die Folge waren unnötige Schreibprozesse.

In PostgreSQL 8.3 werden die wiederkehrenden Scans derjenigen Puffer, die möglicherweise nicht geleert werden können, vermieden. Stattdessen schätzt der Background-Writer ständig ab, wieviele Puffer das System in einem bestimmten Zeitintervall typischerweise allokiert und versucht dann, anhand dieses Durchschnitts, diese Anzahl Puffer zur Verfügung zu stellen, indem diejenigen Puffer weggespeichert werden, auf die am längsten nicht mehr zugegriffen wurde (LRU). Auf diese Weise passt er sich ständig der Systemlast an, die Zahl der I/O-Operationen reduziert sich, wodurch sich die Leistung des Gesamtsystems erhöht.

Asynchronous Commit

Bei einem asynchronen Commit werden die Daten committeter Transaktionen verzögert in die Transaktionslogs (WAL) geschrieben und die Kontrolle noch während dieses Schreibvorgangs an den Benutzer zurückgegeben. Der erfolgreiche Schreibvorgang muss nicht abgewartet werden. Dadurch werden Antwortzeiten verkürzt und der Durchsatz für kleinere Transaktionen wesentlich erhöht.

Damit verbunden ist das Risiko eines möglichen Verlustes der soeben committeten Daten, falls das System genau in dieser Zeitspanne abstürzt und die Daten noch nicht auf die Platte geschrieben wurden.
Asynchrone Commits sind deshalb nur für Anwendungen zu empfehlen, bei denen ein möglicher Datenverlust akzeptabel ist.

Im Gegensatz zu fsync (das für den ganzen Server gilt und das Verhalten aller Transaktionen bestimmt) ist bei einem Asynchronen Commit die Konsistenz der Datenbank nicht gefährdet. Schlimmstenfalls fehlen nach einem Systemabsturz die paar letzten Transaktionen. Der Vorteil ist eine dramatische Leistungssteigerung bei allen Abfragen, in denen Daten modifiziert werden.

Mit der Direktive

synchronous_commit off

und einer Angabe für die Verzögerungszeit in der Direktive

 wal_writer_delay

wird der Asynchrone Commit aktiviert und konfiguriert.

Der Commit-Modus kann für jede Transaktion angegeben werden, es ist also möglich, sowohl synchrone als auch asynchrone Commits nebeneinander auszuführen. Damit hat man die Möglichkeit, Sicherheit und Performance zu balancieren. Maßgebend für den Commit-Modus ist der Wert von synchronous_commit zu Beginn eines Commits.

Synchronized Scans - Sequentielle Scans im *Huckepack*-Verfahren

Vor PostgreSQL 8.3 begann jeder sequentielle Scan einer Tabelle bei Record 0 und durchlief die gesamte Tabelle sequentiell. Performance-Probleme entstanden, wenn mehrere SeqScans derselben Tabelle parallel ausgeführt wurden und die Tabelle so groß war, dass sie nicht komplett gecacht werden konnte. Zwangsläufig waren dann Plattenzugriffe nötig, die die Ausführungsgeschwindigkeit drosselten.

Mit Synchronisierten Scans ist es ab PostgreSQL 8.3 möglich, dass mehrere Benutzer auf einen Sequentiellen Scan einer Tabelle zugreifen können, wodurch Lesezugriffe auf die Platte enorm reduziert werden (zwischen 25 und 75% bei sehr großen Tabellen).

SeqScans können jetzt bei einem beliebigen Datensatz einer Tabelle beginnen. Jeder Prozess hinterlegt im shared memory, an welcher Position ein SeqScan ist. Wenn ein weiterer Prozess einen SeqScan derselben Tabelle anfordert, kann er an genau dieser Position damit beginnen. Dieser zweite Scan kann nun auf Daten zugreifen, die bereits im Cache vorliegen. Vor allem bei Tabellen, die größer sind als der Cache und die deshalb viele I/O-Operationen bedingen, führt dies zu enormen Leistungssteigerungen.

Die Implementierung eines kleinen Ringpuffers, in dem die Daten eines sequentiellen Scans gecacht werden, sorgt zusätzlich für bessere Performance, weil er verhindert, dass andere gecachte Daten ausgelagert werden müssen. Dadurch wird das Caching von PostgreSQL effizienter.

Verteilte Checkpoints

Wenn das System unter hoher Last arbeitet, werden Checkpoint-Daten (WAL-Files) verzögert und verteilt auf die Platte geschrieben, wodurch die Leistungskurve des Gesamtsystems geglättet wird. I/O-Spitzen während eines Checkpoints werden auf diese Weise gemildert.

Vor PostgreSQL 8.3 wurden alle veränderten Puffer bei einem Checkpoint auf die Platte geschrieben, was zu einer I/O-Spitze und zu einem Leistungsabfall im Gesamtsystem führen konnte. Jetzt können die Daten zwischen zwei Checkpoints verzögert gespeichert werden, wodurch I/O Spitzen abgebaut werden und das System weniger belasten. Mit einem neuen Konfigurationsparameter,

checkpoint_completion_target,

kann man dem System die Zeit zur Abarbeitung eines Checkpoints vorgeben.

Weitere Performance-Verbesserungen

Schnellere Merge-Joins

Kleine Merge-Joins wurden durch eine neue Implementierung drastisch beschleunigt, weil keine temporären Daten mehr automatisch auf die Platte geschrieben werden, wenn der Platz im Arbeitsspeicher ausreicht.

Kleinere Header

Die Länge der Header für Datentypen variabler Länge wurde verkürzt, so dass Datenbanken um bis zu 20% kleiner werden. Sind Werte von weniger als 128 Bytes in solchen Feldern gespeichert, reduziert sich die Länge des Headers um 3-6 Bytes. Beispielsweise belegen zwei char(1)-Felder in PostgreSQL 8.3 nur noch 4 Bytes, anstelle von 16 Bytes in älteren Versionen.

Lazy XID Assignment

Alle Transaktionen erhielten in PostgreSQL eine TransaktionsID (XID). Dabei muss sichergestellt werden, dass diese IDs niemals miteinander in Konflikt geraten. Ab PostgreSQL 8.3 erhalten nur noch solche Transaktionen eine ID, die Daten erzeugen oder manipulieren und nur für diese wird eine persistente ID vergeben. Alle anderen Transaktionen erhalten nicht-persistente IDs.

Das Ergebnis ist ein signifikant schnellerer Datendurchsatz bei Datenbanken, in denen viel oder ausschließlich gelesen wird; weil nicht-persistente TransaktionsIDs den globalen Transaktionszähler nicht erhöhen, müssen weniger XIDs vergeben werden, wodurch VACUUMs zur Vermeidung von XID-Überläufen (ID wraparound) seltener werden.

Neue Datentypen

UUID Datentyp

Zum Speichern von Universal Unique Identifiers (UUID oder GUID) steht ab PostgreSQL 8.3 der Datentyp UUID zur Verfügung. Das ist ein String mit einer Länge von 32 Zeichen, der den UUID mit seinen 128 Bit repräsentiert.

Funktionen zum Speichern und Vergleichen von UUIDs sind in PostgreSQL enthalten und im contrib-Modul uuid-ossp werden Funktionen zum Erzeugen von UUIDs angeboten.

XML Datentyp

Mit dem Datentyp xml können XML-Daten in einer PostgreSQL 8.3 Datenbank gespeichert werden. Es können entweder wohlgeformte Dokumente (DOCUMENT) oder auch Fragmente (CONTENT) gespeichert werden. Bei der Speicherung werden die XML-Daten gleichzeitig auf Wohlgeformtheit überprüft. Allerdings werden Inhalte nicht gegen eine eventuell im Dokument vorhandene DTD geprüft.

Voraussetzung für die Benutzung dieses Typs ist, dass PostgreSQL mit der Direktive --with-libxml kompiliert wurde. Interessant ist auch die Möglichkeit, Tabellen oder Abfragen auf XML-Daten abzubilden, was den XML-Export von Tabellen oder Abfrageergebnissen ab PostgreSQL 8.3 ermöglicht.

ENUM

Mit ENUM gibt es auch in PostgreSQL 8.3 einen Datentyp für Mengen. Erzeugt werden diese Typen mit dem Kommando CREATE TYPE. Danach können sie in allen Tabellen oder Funktionen verwendet werden. Eine Reihe von Funktionen erleichtern die Arbeit mit ENUM-Daten. ENUM wird oft als Ersatz für kleine, statische Lookup-Tabellen verwendet.

Tsearch2 - Volltextsuche in PostgreSQL

Tsearch2 wurde in den PostgreSQL 8.3-Kern integriert. Bei der Volltext-Indexierung werden die Dokumente indiziert, damit sie später schneller durchsucht werden können.
Dabei werden die Dokumente geparst und in Tokens zerlegt, die klassifiziert werden können, beispielsweise in Worte, Zahlen ... , die dann unterschiedlich behandelt werden können. PostgreSQL stellt dafür einen Standard-Parser bereit.

Die Token werden anschließend in Lexeme (Wortstämme) zurückgeführt, damit unterschiedliche Beugungsformen, Mehrzahlformen, zusammegesetzte Wörter ... zusammengefasst werden und auffindbar sind. Typischerweise werden in diesem Schritt auch Wörter aussortiert, die zu suchen nicht sinnvoll ist, wie etwa der, die, das, in ... (sog. Stopwörter). PostgreSQL benutzt für diesen Schritt Wörterbücher und stellt mehrere Standard-Dictionaries zur Verfügung. Jedes Dokument kann danach als sortiertes Array von Lexemen repräsentiert werden.

Zur Speicherung so vorbereiteter Dokumente gibt es den Datentyp tsvector und zur Darstellung von Suchabfragen gibt es den Datentyp tsqery. Um Volltextsuchen zu beschleunigen, kann ein invertierter Index (GIN) definiert werden. Dabei wird für jedes Lexem, das in einem Dokument vorkommt, eine Liste aller Dokumente erstellt, in denen dieses Lexem auftritt.

Administration und Entwicklung

CSV-Logs

PostgreSQL unterstützt mehrere Methoden zur Protokollierung der Meldungen des Datenbankservers. Seit der Version 8.3 wird auch die Protokollierung im CSV-Format unterstützt. Somit können diese Logs einfach in andere Anwendungen oder in Tabellen importiert werden.

GSSAPI und SSPI Unterstützung

PostgreSQL 8.3 unterstützt GSSAPI zur Authentifizierung. So kann PostgreSQL einfacher in bestehende Projekte eingebunden werden, die diese Authentifizierung verwenden. Auf Windows wird SSPI sowohl für Kerberos als auch für NTLM unterstützt.

Mehrere autovacuum-Prozesse

Ab PostgreSQL 8.3 kann autovacuum mehrere Prozesse initiieren. Auf diese Weise können mehrere Tabellen gleichzeitig bereinigt werden. Dies ist beispielsweise von Vorteil, wenn man neben großen auch kleine, häufig aktualisierte Tabellen hat, die übermäßig anwuchsen, weil sie warten mussten, bis autovacuum die großen Tabellen abgearbeitet hatte.

Create table like including indexes

Mit LIKE kann man eine Tabelle angeben, von der alle Feldnamen und -typen sowie deren NOT-NULL-Constraints übernommen werden. Mit der neuen Option INCLUDING INDEXES können auch alle Indexe, die für die Originaltabelle definiert waren, in die neue Tabelle oder Partition übernommen werden.

Benutzerspezifische Einstellungen für Funktionen

Wenn Funktionen erzeugt werden, können mit der neuen Option *SET configuration_parameter* benutzerspezifische Einstellungen (GUC - Global User Configuration) integriert werden. Diese Konfigurationsparameter werden geladen, wenn die Funktion aufgerufen wird. Nachdem die Funktion abgearbeitet ist, werden die vorherigen Werte wieder restauriert.

Arrays aus zusammengesetzten Datentypen

Arrays können nun Elemente aus zusammengesetzten Datentypen haben, beispielsweise Zeilen aus Tabellen oder Views oder benutzerdefinerte Typen. Damit wird die Übergabe oder Rückgabe von Daten an bzw. von benutzerdefinierten Funktionen flexibler.

Bessere Ausführungspläne bei benutzerdefinierten Funktionen

Beim Erzeugen einer Funktion kann man eine geschätzte Ausführungszeit und die erwartete Menge von Datensätzen angeben, um dem Planner Anhaltspunkte zur Erzeugung besserer Ausführungspläne zu geben.

Ausführungspläne löschen

Gecachte Ausführungspläne für Funktionen werden ab PostgreSQL 8.3 automatisch gelöscht, wenn Schemaänderungen an referenzierten Tabellen vorgenommen werden. Davor war dies nur möglich, wenn man entweder die Datenbanksitzung beendete oder die Funktion neu definierte.