PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Leistung von Sequenzen und Serien in Postgres-XL

In Postgres-XL werden Sequenzen im Global Transaction Manager (GTM) verwaltet, um sicherzustellen, dass ihnen widersprüchliche Werte zugewiesen werden, wenn sie von mehreren Knoten inkrementiert werden. Dies fügt einen erheblichen Overhead für eine Abfrage hinzu, die Tausende von INSERTs in einer Tabelle mit einer seriellen Spalte durchführt, die Sequenz einzeln erhöht und für jede INSERT einen Netzwerk-Roundtrip zum GTM durchführt.

Shaun Thomas hat sich kürzlich in einem Blog darüber beschwert, dass INSERTs auf Postgres-XL im Vergleich zu Vanilla PostgreSQL um eine Größenordnung langsamer laufen. Es gibt bereits eine Möglichkeit, die Leistung für Sequenzen zu verbessern, aber es wird eindeutig nicht gut beworben. Ich dachte, das ist eine gute Gelegenheit, die Einrichtung zu erklären.

Postgres-XL bietet eine vom Benutzer einstellbare GUC namens sequence_range . Jedes Backend fordert einen Block von Sequenzwerten an, die von diesem GUC gesteuert werden. Angesichts der Tatsache, dass COPY häufig zum Massenladen von Daten in Postgres verwendet wird, überschreibt Postgres-XL diese GUC während des COPY-Vorgangs automatisch und setzt sie auf 1000, wodurch die COPY-Leistung erheblich verbessert wird. Leider ist der Standardwert für reguläre INSERTs 1, es sei denn, der Benutzer legt ausdrücklich sequence_range fest auf einen angemessen höheren Wert, leidet die INSERT-Leistung. Hier ist ein Beispiel, das dasselbe Musterschema verwendet, das Shaun in seinem Blogbeitrag verwendet hat.

CREATE TABLE sensor_log (
  sensor_log_id  SERIAL PRIMARY KEY,
  location       VARCHAR NOT NULL,
  reading        BIGINT NOT NULL,
  reading_date   TIMESTAMP NOT NULL
) DISTRIBUTE BY HASH (sensor_log_id);

postgres=# \timing
Timing is on.
postgres=# INSERT INTO sensor_log (location, reading, reading_date)                                                                                                                         SELECT s.id % 1000, s.id % 100, now() - (s.id || 's')::INTERVAL                                                                                                                    FROM generate_series(1, 40000) s(id);
INSERT 0 40000
Time: 12067.911 ms

postgres=# set sequence_range TO 1000;
SET
Time: 1.231 ms
postgres=# INSERT INTO sensor_log (location, reading, reading_date)                                                                                                                         SELECT s.id % 1000, s.id % 100, now() - (s.id || 's')::INTERVAL                                                                                                                    FROM generate_series(1, 40000) s(id);
INSERT 0 40000
Time: 397.406 ms

Also durch entsprechendes Setzen von sequence_range auf 1000, Leistung der INSERT-Abfrage um fast das 30-fache verbessert.

Als diese Funktion hinzugefügt wurde, wurde der Standardwert von sequence_range GUC auf 1 gesetzt, da dies Lücken in den Sequenzwerten hinterlassen kann. Aber angesichts der Leistungsauswirkungen für einen sehr häufigen Anwendungsfall haben wir uns entschieden, den Standardwert auf 1000 zu erhöhen, und dies wurde nun in den XL9_5_STABLE-Zweig des Repositorys übernommen.

Es ist wichtig zu beachten, dass ein hoher Wert von sequence_range verbessert die Leistung für Sequenzen und Zeitschriften, kann aber auch große Lücken in Sequenzbereichen hinterlassen, da die Sequenzbereiche auf Backend-Ebene zwischengespeichert werden. Um dieses Problem zu beheben, beginnt Postgres-XL mit dem angegebenen CACHE-Parameterwert, der zum Zeitpunkt der Sequenzerstellung verwendet wird, und verdoppelt ihn jedes Mal (begrenzt durch sequence_range), wenn Sequenzen mit einer sehr hohen Rate verbraucht werden.

Eine ähnliche Verbesserung kann auch erreicht werden, indem der CACHE-Parameterwert der Sequenz erhöht wird, sodass ein Teil der Sequenzwerte auf der Backend-Ebene zwischengespeichert wird. Das folgende Beispiel zeigt, wie das für eine serielle Spalte gemacht wird. Aber der sequence_range GUC bietet eine einfache Möglichkeit, den globalen Standard zu überschreiben, und stellt außerdem sicher, dass die Sequenzen nur zwischengespeichert werden, wenn sie sehr schnell inkrementiert werden.

postgres=# ALTER SEQUENCE sensor_log_sensor_log_id_seq CACHE 1000;                                                                                                             ALTER SEQUENCE
Time: 8.683 ms
postgres=# SET sequence_range TO 1;
SET
Time: 2.341 ms
postgres=# INSERT INTO sensor_log (location, reading, reading_date)                                                                                                            SELECT s.id % 1000, s.id % 100, now() - (s.id || 's')::INTERVAL                                                                                                                  FROM generate_series(1, 40000) s(id);
INSERT 0 40000
Time: 418.068 ms

Sie können eine dieser Techniken wählen, um die Leistung zu verbessern. Obwohl jetzt der Standardwert von sequence_range auf 1000 geändert wird, sehen möglicherweise nicht viele Benutzer den Leistungsunterschied.