Speicherplatz für PostgreSQL-B-Baum-Indexe richtig nutzen

Wider die Blasen

Obwohl viele PostgreSQL-Benutzer ihre Datenbank regelmäßig mit VACUUM aufräumen, und obwohl die Tabellen kaum wachsen, kann der B-Baum-Index immer mehr Speicherplatz belegen. Denn auch hier ist der Admin in der Pflicht und muss vorsorgen.
NAS-Speicher mit einer Kapazität von einigen Dutzend Terabyte, wie sie sich für mittelständische Anwender eignen, nimmt die ADMIN-Redaktion in der Ausgabe ... (mehr)

Genau wie Tabellen speichert PostgreSQL auch die Daten von Indexen, die mit dem B-Tree-Verfahren erzeugt wurden, immer in 8 kByte großen Pages (solange die Pagegröße beim Kompilieren nicht geändert wurde). Die Anzahl an Pages, die Indexe und Tabellen für sich in Anspruch nehmen, sowie die Anzahl der abgelegten Tupel lässt sich einfach mithilfe der PostgreSQL-internen Tabelle »pg_class« herausfinden:

SELECT relname, reltuples, relpages
FROM pg_class
WHERE relname LIKE 't\_%';
 relname | reltuples | relpages
-----------+-----------+----------
 t_index | 10000 | 30
 t_tabelle | 10000 | 45

Zu sehen ist, dass der Index 30 und die Tabelle selbst 45 Pages in Anspruch nimmt. Wird die Anzahl der Pages mit 8 kByte multipliziert, so ergibt das den tatsächlichen Speicherverbrauch in kByte. Wenn alle Datensätze einer Tabelle gelöscht werden sollen, kommt häufig die Frage auf, was der Unterschied zwischen »DELETE« und »TRUNCATE« ist. Tatsächlich gibt es gleich zwei gravierende Unterschiede: Erstens gibt »DELETE« den Speicherplatz für eine Tabelle erst nach einem »VACUUM« an das System zurück – »TRUNCATE« dagegen sofort. Zweitens: TRUNCATE befreit auch den Index vom Ballast.

Ein kleines Experiment verdeutlicht das (Listing 1). Dort ist zu sehen, dass der Speicherplatz für die Tabelle erst nach »VACUUM ANALYZE« vollständig an das System zurückgegeben wurde. Der Index jedoch beansprucht danach immer noch 30 Pages für sich. Werden jetzt wieder 10000 Werte in die Tabelle eingespielt, legt die Datenbank für den Index 27 weitere Pages neu an, statt die vorhandenen 30 auszunutzen. Obwohl hier lediglich wieder genauso viele Zeilen eingefügt wie vorher gelöscht wurden, ist der Index fast doppelt so groß geworden. »DELETE« plus »VACUUM« reichen also nicht aus, um den Indexspeicher auf das kleinstmögliche Maß zu schrumpfen.

Listing 1

Löschen leert den Index nicht

01 DELETE FROM t_tabelle;
02 ANALYZE;
03
04   relname  | reltuples | relpages
05 -----------+-----------+----------
06  t_tabelle |         0 |       45
07  t_index   |         0 |       30
08
09 VACUUM ANALYZE;
10
11  relname  | reltuples | relpages
12 -----------+-----------+----------
13  t_tabelle |         0 |        0
14  t_index   |         0 |       30
15
16
17 # 1000 Datensätze einfügen ...
18
19   relname  | reltuples | relpages
20 -----------+-----------+----------
21  t_tabelle |     10000 |       45
22  t_index   |     10000 |       57

»TRUNCATE« gibt den Speicher sofort und unverzüglich zurück an das System (Listing 2). »TRUNCATE« hat nicht nur den Speicherplatz der Tabelle an das System zurückgegeben, sondern bis auf eine Page auch den Index vom Ballast befreit. Fügt man danach wie oben wieder 1000 Datensätze ein, ist der Index wieder so groß wie zu Anfang – und nicht verdoppelt, wie im ersten Beispiel.

Listing 2

Index bereinigen mit TRUNCATE

01      relname       | reltuples | relpages
02 --------------------+-----------+----------
03  t_truncate_index   |     10000 |       30
04  t_truncate_tabelle |     10000 |       45
05
06 TRUNCATE t_truncate_index;
07 ANALYZE;
08
09       relname       | reltuples | relpages
10 --------------------+-----------+----------
11  t_truncate_index   |         0 |        1
12  t_truncate_tabelle |         0 |        0
13
14
15 # 1000 Datensätze einfügen ...
16
17      relname       | reltuples | relpages
18 --------------------+-----------+----------
19  t_truncate_index   |     10000 |       30
20  t_truncate_tabelle |     10000 |       45

Minimum ist eine Page

Aber warum hat Truncate beim Index eine Page stehen lassen? Die eine Page wird auch angelegt, wenn auf einer frischen, leeren Tabelle ein Index erzeugt wird: Ein Index nimmt immer mindestens 8 kByte in Anspruch. Diese erstePage enthält wichtige Metainformation. Jeder Index, egal ob leer oder nicht, braucht diese Page. Hier wird festgehalten, wo sich die Wurzel des Baumes befindet. Der eigentliche Indexbaum beginnt erst mit der zweiten Page. Wie sieht ein solcher B-Baum in PostgreSQL aus? Der B-Baum ist eine Datenstruktur, die schon 1972 von Rudolf Bayer, damals für Boeing tätig, und Edward M. McCreight entwickelt wurde. Die Entwickler haben bis heute nicht verraten, wofür das "B" steht. Die Interpretationen gehen von "Balanciert" über "Bayer" und "Boeing" bis hin zu "Barbara" (Bayers Frau). Für die Verwendung zum Speichern von Datenbank-Indexen warf der B-Baum-Algorithmus jedoch zunächst einige Fragen auf: Was ist mit zeitgleichen Zugriffen? Was ist mit Locking? Wie ist er MVCC-konform zu implementieren? Wie soll er sich beim Löschen verhalten? Über diese Themen haben unter anderem Phillip L. Lehman und S. Bing Yao sowie Vladimir Lanin und Dennis Shasha Abhandlungen geschrieben, die bei der PostgreSQL-Implementierung Berücksichtigung fanden. Bei der PostgreSQL-B-Baum-Implementierung handelt es sich um eine korrekte Implementierung des Lehman und Yao High-Concurrency B-Tree Management Algorithmus sowie einer vereinfachten Version der Lösch-Logik, die Lanin und Shasha beschrieben haben.

Einfaches Prinzip

Das Prinzip des B-Baums ist einfach. Ganz unten befinden sich die Blätter (Leaves). Sie enthalten neben den indizierten Datenwerten (in Hex) den Link zu dem Tupel der zugehörigen Tabelle. Das obere Ende des Baumes markiert die Wurzel, in der ebenfalls ein Schlüsselwert hinterlegt ist. Es kann immer nur eine Wurzel geben. Die Ebenen zwischen Wurzel und Blättern enthalten innere Knoten, die den Baum in Unterbäume gliedern. Die inneren Knoten enthalten ebenfalls Schlüsselwerte, anhand derer der weitere Weg zu erkennen ist. Alle Schlüsselwerte, die kleiner als der Schlüssel der Wurzel sind, befinden sich auf der linken Seite des Baumes, alle anderen auf der rechten Seite. Abbildung 1 zeigt wie ein B-Baum aussehen könnte.

Abbildung 1: So könnte ein B-Baum-Index aussehen.

Wie viele Einträge auf eine Page passen, hängt von den indizierten Datensätzen ab. Bei einem Index über eine einzelne Integer-Spalte können durchaus mehrere Hundert auf eine Page passen. Der implementierte Algorithmus sieht jedoch vor, dass mindestens drei Einträge auf die Leave Pages passen müssen. PostgreSQL kann die Funktionalität nicht sicherstellen, wenn die Datensätze größer als 1/3 der Pagegröße sind. Um leere Pages in der Mitte zu vermeiden, muss eine linke Page immer mindestens zwei Datensätze enthalten. Wird der vorletzte Dateneintrag entfernt, sorgt das nächste »VACUUM« für eine Umsortierung.

comments powered by Disqus

Artikel der Woche

Zertifikatsmanagement mit Certmonger

Zertifikate werden dazu verwendet, um Benutzer, Services und Hardware mit der Hilfe eines signierten Schlüssels zu verifizieren. Hierfür existieren Public-Key-Infrastrukturen (PKI). Aber wie gelangen die Zertifikate eigentlich auf das Ziel-Gerät? Der Open-Source-Tipp in diesem Monat beschreibt, wie Sie für diese Aufgabe das Tool certmonger verwenden können. (mehr)
Einmal pro Woche aktuelle News, kostenlose Artikel und nützliche ADMIN-Tipps.
Ich habe die Datenschutzerklärung gelesen und bin einverstanden.

Linux-Backup

Welche Backup-Lösung setzen Sie für Linux im professionellen Umfeld ein?

  • keine
  • eigene Scripts
  • rsnapshot
  • rdiff-backup
  • Bacula
  • Bareos
  • Borg
  • Duplicity
  • Amanda
  • Burp
  • Clonezilla
  • Acronis
  • Arkeia
  • SEP sesam
  • Veeam
  • Redo Backup
  • Relax-and-Recover
  • andere kommerzielle Software
  • andere Open-Source-Software

Google+

Ausgabe /2017

Microsite