Könnte mir jemand dieses Verhalten erklären? Ich habe die folgende Abfrage unter Postgres 9.3 ausgeführt, das nativ unter OS X ausgeführt wird. Ich habe versucht, ein Verhalten zu simulieren, bei dem die Indexgröße viel größer als die Tabellengröße werden kann, und stattdessen etwas noch Seltsameres gefunden.
CREATE TABLE test(id int);
CREATE INDEX test_idx ON test(id);
CREATE FUNCTION test_index(batch_size integer, total_batches integer) RETURNS void AS $$
DECLARE
current_id integer := 1;
BEGIN
FOR i IN 1..total_batches LOOP
INSERT INTO test VALUES (current_id);
FOR j IN 1..batch_size LOOP
UPDATE test SET id = current_id + 1 WHERE id = current_id;
current_id := current_id + 1;
END LOOP;
END LOOP;
END;
$$ LANGUAGE plpgsql;
SELECT test_index(500, 10000);
Ich ließ dies ungefähr eine Stunde lang auf meinem lokalen Computer laufen, bevor ich von OS X Warnungen zu Festplattenproblemen erhielt. Ich bemerkte, dass Postgres ungefähr 10 MB / s von meiner lokalen Festplatte aufsaugte und dass die Postgres-Datenbank eine Gesamtsumme verbrauchte von 30 GB von meinem Computer. Am Ende habe ich die Abfrage abgebrochen. Unabhängig davon hat Postgres den Speicherplatz nicht an mich zurückgegeben, und ich habe die Datenbank nach Nutzungsstatistiken mit dem folgenden Ergebnis abgefragt:
test=# SELECT nspname || '.' || relname AS "relation",
pg_size_pretty(pg_relation_size(C.oid)) AS "size"
FROM pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_relation_size(C.oid) DESC
LIMIT 20;
relation | size
-------------------------------+------------
public.test | 17 GB
public.test_idx | 14 GB
Die Auswahl aus der Tabelle ergab jedoch keine Ergebnisse.
test=# select * from test limit 1;
id
----
(0 rows)
Das Ausführen von 10000 500er-Batches umfasst 5.000.000 Zeilen, was eine ziemlich kleine Tabellen- / Indexgröße (auf der MB-Skala) ergeben sollte. Ich vermute, dass Postgres für jedes INSERT / UPDATE, das mit der Funktion geschieht, eine neue Version der Tabelle / des Index erstellt, aber das scheint seltsam. Die gesamte Funktion wird transaktional ausgeführt, und die Tabelle war zum Starten leer.
Irgendwelche Gedanken darüber, warum ich dieses Verhalten sehe?
Die beiden Fragen, die ich habe, lauten insbesondere: Warum wurde dieser Speicherplatz noch nicht von der Datenbank zurückgefordert, und die zweite Frage ist, warum die Datenbank überhaupt so viel Speicherplatz benötigt hat. 30 GB scheinen viel zu sein, selbst wenn MVCC berücksichtigt wird
quelle
Die tatsächlichen Zahlen nach der Analyse der Funktion sind viel größer, da alle Zeilen der Tabelle denselben Wert erhalten, der in jeder Iteration mehrmals aktualisiert wird.
Wenn wir es mit Parametern ausführen
n
undm
:Es gibt
m
Zeileneinfügungen undn * (m^2 + m) / 2
Aktualisierungen. Fürn = 500
undm = 10000
muss Postgres also nur 10K-Zeilen einfügen, aber ~ 25G (25 Milliarden) Tupel-Updates durchführen.Wenn man bedenkt, dass eine Zeile in Postgres einen Overhead von 24 Bytes hat, benötigt eine Tabelle mit nur einer einzelnen
int
Spalte 28 Bytes pro Zeile plus den Overhead der Seite. Für den Abschluss des Vorgangs benötigen wir also etwa 700 GB plus Speicherplatz für den Index (dies wären auch einige hundert GB).Testen
Um die Theorie zu testen, haben wir eine weitere Tabelle
test_test
mit einer einzelnen Zeile erstellt.Dann fügen wir einen Trigger hinzu,
test
damit bei jedem Update der Zähler um 1 erhöht wird (Code weggelassen). Dann führen wir die Funktion mit kleineren Werten ausn = 50
undm = 100
.Unsere Theorie sagt voraus :
Test 1 (Originaltabelle
test
mit Index)Nach Abschluss überprüfen wir den Tabelleninhalt:
Und Festplattennutzung (Abfrage unter Indexgröße / Nutzungsstatistik in der Indexpflege ):
Die
test
Tabelle hat fast 9 MB für die Tabelle und 5 MB für den Index verwendet. Beachten Sie, dass dietest_test
Tabelle weitere 9 MB verwendet hat! Dies wird erwartet, da auch 250.000 Aktualisierungen durchgeführt wurden (unser zweiter Trigger hat die einzelne Zeile vontest_test
für jede Aktualisierung einer Zeile in aktualisierttest
.)Beachten Sie auch die Anzahl der Scans in der Tabelle
test
(10 KB) und die gelesenen Tupel (500 KB).Test 2 (
test
Tabelle ohne Index)Genau das gleiche wie oben, außer dass die Tabelle keinen Index hat.
Wir erhalten die gleiche Größe für die Festplattennutzung der Tabelle und natürlich keine Festplattennutzung für Indizes. Die Anzahl der Scans in der Tabelle
test
ist jedoch Null und die Tupel lesen ebenfalls.Test 3 (mit niedrigerem Füllfaktor)
Versucht mit Füllfaktor 50 und dem niedrigstmöglichen, 10. Überhaupt keine Verbesserung. Die Datenträgernutzung war fast identisch mit den vorherigen Tests (bei denen der Standardfüllfaktor 100 Prozent verwendet wurde).
quelle