2010-12-03 7 views
6

Ich habe ein Skript, das Zehntausende von Einfügungen in eine Postgres db durch eine benutzerdefinierte ORM generiert. Wie du dir vorstellen kannst, ist es ziemlich langsam. Dies wird für Entwicklungszwecke verwendet, um Dummydaten zu erstellen. Gibt es eine einfache Optimierung, die ich auf der Postgres-Ebene machen kann, um das schneller zu machen? Es ist das einzige Skript, das sequenziell ausgeführt wird und keine Thread-Sicherheit erfordert.Postgres einfügen Optimierung

Vielleicht kann ich alle Sperren, Sicherheitsüberprüfungen, Trigger usw. ausschalten? Suchen Sie einfach nach einer schnellen und schmutzigen Lösung, die diesen Prozess erheblich beschleunigt.

Danke.

Antwort

8

Wenn Sie diese Funktionalität in der Produktionsumgebung nicht benötigen, rate ich Ihnen, fsync aus Ihrer PostgreSQL-Konfiguration auszuschalten. Dies wird die Einsätze dramatisch beschleunigen.

Deaktivieren Sie niemals fsync in einer Produktionsdatenbank.

+1

übernimmt. Ich stimme zu: fsync sollte in der Produktion niemals abgeschaltet werden (es sei denn ein sehr zuverlässiger batteriegestützter Controller). Aber synchronous_commit = false könnte tatsächlich Dinge verbessern und stellt kein großes Risiko dar. –

+0

In meiner Testumgebung 'synchrone_commit' hat Geschwindigkeit nicht genug verbessert, um von Unterschied zu sein. IIRC Dies schnitt einen 2-minütigen DB-Erstellungs- und Auffüllungsprozess auf die Hälfte, aber das Ausschalten von fsync ließ es in 10 Sekunden laufen. Ich habe keine Zehntausende von Datensätzen, so dass meine Testdatenbank niemals mit 'fsync = off' auf die Disc trifft. – jmz

+3

Ein batteriegestützter Cache kann Sie nicht davon abhalten, fsync ausgeschaltet zu haben! Wenn Ihr Betriebssystem abstürzt ODER nach einem gefälschten fsync Strom verliert, bevor Daten auf eine Festplatte geschrieben werden, verlieren Sie Daten. Es gibt auch Fragen zu vollständigen Seitenschreibvorgängen, die jetzt selbst bei BBU-Caching-RAID-Controllern zu 100% sicher sind. –

3

Eine Sache, die Sie tun können, ist alle Indizes zu entfernen, Ihre Einfügungen zu tun und dann die Indizes neu zu erstellen.

2

Senden Sie einen Stapel von Zehntausenden von INSERTs OR senden Sie Zehntausende von INSERTs?

Ich weiß, mit Hibernate können Sie alle Ihre SQL-Anweisungen Batch-und senden sie am Ende in einem großen Brocken, anstatt die Steuer von Netzwerk-und Datenbank-Overhead Tausende von SQL-Anweisungen einzeln zu machen.

8

Der schnellste Weg zum Einfügen von Daten wäre der COPY Befehl. Aber das erfordert eine flache Datei als Eingabe. Ich denke, das Generieren einer flachen Datei ist keine Option.

Nicht zu oft verpflichten, vor allem nicht führen Sie dies mit Autocommit aktiviert. "Zehntausende" klingt wie ein einziges Commit am Ende wäre genau richtig.

Wenn Sie Ihre ORM convice Verwendung von Postgres' mehrreihigen Einsatz zu machen, die Dinge beschleunigen würde auch

Dies ist ein Beispiel für eine mehrreihige einfügen:

 
insert into my_table (col1, col2) 
values 
(row_1_col_value1, row_1_col_value_2), 
(row_2_col_value1, row_2_col_value_2), 
(row_3_col_value1, row_3_col_value_2) 

Wenn Sie können nicht die obige Syntax generieren und Sie verwenden Java stellen Sie sicher, batched Aussagen statt einzelne Anweisung Einsätze (vielleicht andere DB Schichten erlauben so ähnlich)

bearbeiten verwenden:

jmz 'Beitrag inspirierte mich, etwas hinzuzufügen:

Sie könnten auch eine Verbesserung sehen, wenn Sie wal_buffers auf einen größeren Wert erhöhen (z. 8MB) und checkpoint_segments (z. B. 16)

+0

+1 für COPY. Bester Ansatz für Geschwindigkeit. – karlgrz

+1

Der Befehl copy benötigt KEINE flache Datei, da er die Eingabe von standard in annehmen kann. Erstellen Sie ein Klartext-Backup Ihrer db und Sie werden es voll von Kopierbefehlen mit stdin sehen. –

+2

@Scott: Sie haben Recht. Aber das Format ist immer noch ein "Klartext" -Format. Um also den schnellen COPY-Mechanismus auszunutzen, ist der Aufwand, das existierende Programm neu zu schreiben, im Wesentlichen gleich, ob COPY es von einer Datei oder von stdin –

6

Für Einsätze diese Zahl in die Hunderte bis Tausende, batch sie:

begin; 
insert1 ... 
insert2 ... 
... 
insert10k ... 
commit; 

Für Einsätze in die Millionen verwenden Kopie:

COPY test (ts) FROM stdin; 
2010-11-29 22:32:01.383741-07 
2010-11-29 22:32:01.737722-07 
... 1Million rows 
\. 

Stellen Sie sicher, jede Spalte als fk in einer anderen Tabelle verwendet wird indexiert, wenn es in der anderen Tabelle mehr als trivial ist.

2

Wenn Sie nur konstant Testdaten zu initialisieren, können Sie auch die Testdaten in eine Zwischenspeichertabelle gesetzt (s), dann kopieren Sie einfach den Tabelleninhalt, mit

INSERT INTO... SELECT... 

die etwa so schnell sein sollte als mit COPY (obwohl ich es nicht benchmarkte), mit dem Vorteil, dass Sie nur mit SQL-Befehlen kopieren können, ohne dass Sie eine externe Datei wie für COPY einrichten müssen.

2

Versuchen Sie so viel wie möglich in einer Anfrage zu tun!

insert into my_table (col1, col2) 
values (
    unnest(array[row_1_col_value_1, row_2_col_value_1, row3_col_value_1]), 
    unnest(array[row_1_col_value_2, row_2_col_value_2, row_3_col_value_2)); 

Dies ähnelt dem Vorschlag von @a_horse_with_no_name. Der Vorteil der Verwendung von unnest ist: Sie können Abfrageparameter verwenden, die Arrays enthalten!

insert into my_table (col1, col2) 
values (unnest(:col_values_1), unnest(:col_values_2)); 

von drei insert Aussagen in einem zusammenbrechenden, sparen Sie mehr als 50% der Ausführungszeit. Und durch die Verwendung von Abfrageparametern mit 2000 Werten in einem einzigen Insert, bekomme ich einen Geschwindigkeitsfaktor von 150 in meiner Anwendung.