Mengoptimalkan kinerja pembaruan massal dalam PostgreSQL

37

Menggunakan PG 9.1 di Ubuntu 12.04.

Saat ini diperlukan waktu hingga 24 jam bagi kami untuk menjalankan serangkaian besar pernyataan UPDATE pada database, yang berbentuk:

UPDATE table
SET field1 = constant1, field2 = constant2, ...
WHERE id = constid

(Kami hanya menimpa bidang objek yang diidentifikasi oleh ID.) Nilai-nilai tersebut berasal dari sumber data eksternal (belum ada dalam DB dalam tabel).

Tabel memiliki beberapa indeks masing-masing dan tidak ada batasan kunci asing. Tidak ada KOMIT yang dibuat sampai akhir.

Diperlukan 2 jam untuk mengimpor a pg_dumpdari seluruh DB. Ini terlihat seperti garis dasar yang harus kita targetkan secara wajar.

Kurang menghasilkan program khusus yang entah bagaimana merekonstruksi set data untuk PostgreSQL untuk diimpor kembali, adakah yang bisa kita lakukan untuk membawa kinerja UPDATE massal lebih dekat dengan impor? (Ini adalah area yang kami yakini dapat menangani pohon gabungan dengan struktur log, tapi kami bertanya-tanya apakah ada yang bisa kami lakukan di dalam PostgreSQL.)

Beberapa ide:

  • menjatuhkan semua indeks non-ID dan membangun kembali sesudahnya?
  • meningkatkan checkpoint_segments, tetapi apakah ini benar-benar membantu throughput jangka panjang yang berkelanjutan?
  • menggunakan teknik yang disebutkan di sini ? (Muat data baru sebagai tabel, lalu "gabungkan" data lama di mana ID tidak ditemukan dalam data baru)

Pada dasarnya ada banyak hal untuk dicoba dan kami tidak yakin apa yang paling efektif atau jika kita mengabaikan hal-hal lain. Kami akan menghabiskan beberapa hari berikutnya bereksperimen, tetapi kami pikir kami akan bertanya di sini juga.

Saya memiliki beban bersamaan di atas meja tetapi hanya-baca.

Yang
sumber
Informasi penting tidak ada dalam pertanyaan Anda: Versi Postgres Anda? Dari mana nilai-nilai itu berasal? Kedengarannya seperti file di luar basis data, tetapi mohon klarifikasi. Apakah Anda memiliki beban bersamaan pada tabel target? Jika ya, apa tepatnya? Atau bisakah Anda sanggup jatuh dan menciptakan kembali? Tidak ada kunci asing, ok - tetapi apakah ada objek tergantung lainnya seperti tampilan? Harap edit pertanyaan Anda dengan informasi yang hilang. Jangan memerasnya dalam komentar.
Erwin Brandstetter
@ ErwinBrandstetter Terima kasih, memperbarui pertanyaan saya.
Yang
Saya berasumsi Anda telah memeriksa melalui explain analyzebahwa itu menggunakan indeks untuk pencarian?
rogerdpack

Jawaban:

45

Asumsi

Karena informasi tidak ada dalam Q, saya akan menganggap:

  • Data Anda berasal dari file di server database.
  • Data diformat seperti halnya COPYoutput, dengan unik id per baris untuk mencocokkan tabel target.
    Jika tidak, format terlebih dahulu dengan benar atau gunakan COPYopsi untuk menangani format.
  • Anda memperbarui setiap baris di tabel target atau sebagian besar.
  • Anda dapat menjatuhkan dan membuat ulang tabel target.
    Itu berarti tidak ada akses bersamaan. Lain pertimbangkan jawaban terkait ini:
  • Tidak ada objek bergantung sama sekali, kecuali untuk indeks.

Larutan

Saya sarankan Anda pergi dengan pendekatan yang sama seperti yang diuraikan di tautan dari peluru ketiga Anda . Dengan optimasi besar.

Untuk membuat tabel sementara, ada cara yang lebih sederhana dan lebih cepat:

CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;

Satu besar UPDATEdari tabel sementara di dalam database akan lebih cepat daripada pembaruan individu dari luar database dengan beberapa urutan besarnya.

Dalam model MVCC PostgreSQL , UPDATEsarana untuk membuat versi baris baru dan menandai yang lama sebagai dihapus. Itu tentang semahal INSERTdan DELETEgabungan. Plus, itu membuat Anda dengan banyak tupel mati. Karena Anda memperbarui seluruh tabel, bagaimanapun, akan lebih cepat secara keseluruhan untuk hanya membuat tabel baru dan menjatuhkan yang lama.

Jika Anda memiliki cukup RAM, atur temp_buffers(hanya untuk sesi ini!) Cukup tinggi untuk menampung tabel temp di RAM - sebelum Anda melakukan hal lain.

Untuk mendapatkan perkiraan berapa banyak RAM yang dibutuhkan, jalankan tes dengan sampel kecil dan gunakan fungsi ukuran objek db :

SELECT pg_size_pretty(pg_relation_size('tmp_tbl'));  -- complete size of table
SELECT pg_column_size(t) FROM tmp_tbl t LIMIT 10;  -- size of sample rows

Script lengkap

SET temp_buffers = '1GB';        -- example value

CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;

COPY tmp_tbl FROM '/absolute/path/to/file';

CREATE TABLE tbl_new AS
SELECT t.col1, t.col2, u.field1, u.field2
FROM   tbl     t
JOIN   tmp_tbl u USING (id);

-- Create indexes like in original table
ALTER TABLE tbl_new ADD PRIMARY KEY ...;
CREATE INDEX ... ON tbl_new (...);
CREATE INDEX ... ON tbl_new (...);

-- exclusive lock on tbl for a very brief time window!
DROP TABLE tbl;
ALTER TABLE tbl_new RENAME TO tbl;

DROP TABLE tmp_tbl; -- will also be dropped at end of session automatically

Beban serentak

Operasi bersamaan di atas meja (yang saya singkirkan dalam asumsi di awal) akan menunggu, setelah meja dikunci di dekat akhir dan gagal segera setelah transaksi dilakukan, karena nama tabel diselesaikan untuk OID segera, tetapi tabel baru memiliki OID yang berbeda. Tabel tetap konsisten, tetapi operasi bersamaan dapat memperoleh pengecualian dan harus diulang. Detail dalam jawaban terkait ini:

Rute UPDATE

Jika Anda (harus) pergi UPDATErute, jatuhkan indeks apa pun yang tidak diperlukan selama pembaruan dan buat kembali sesudahnya. Jauh lebih murah untuk membuat indeks dalam keadaan utuh daripada memperbaruinya untuk setiap baris. Ini juga memungkinkan pembaruan HOT .

Saya menguraikan prosedur serupa menggunakan UPDATEdalam jawaban terkait erat pada SO .

 

Erwin Brandstetter
sumber
1
Saya sebenarnya hanya memperbarui 20% dari baris dalam tabel target - tidak semua, tetapi sebagian yang cukup besar bahwa penggabungan mungkin lebih baik daripada pencarian pembaruan acak.
Yang
1
@AryehLeibTaurog: Itu seharusnya tidak terjadi sejak DROP TABLEmengeluarkan sebuah Access Exclusive Lock. Either way, saya sudah mendaftar prasyarat di bagian atas jawaban saya: You can afford to drop and recreate the target table.Mungkin membantu untuk mengunci meja pada awal transaksi. Saya sarankan Anda memulai pertanyaan baru dengan semua perincian yang relevan dengan situasi Anda sehingga kami dapat menyelesaikan ini.
Erwin Brandstetter
1
@ErwinBrandstetter Menarik. Tampaknya tergantung pada versi server. Saya telah mereproduksi kesalahan pada 8.4 dan 9.1 menggunakan adaptor psycopg2 dan menggunakan klien psql . Pada 9.3 tidak ada kesalahan. Lihat komentar saya di skrip pertama. Saya tidak yakin apakah ada pertanyaan untuk dikirim di sini, tetapi mungkin ada baiknya meminta beberapa informasi pada salah satu daftar postgresql.
Aryeh Leib Taurog
1
Saya menulis kelas pembantu sederhana dengan python untuk mengotomatiskan proses.
Aryeh Leib Taurog
3
Jawaban yang sangat berguna. Sebagai sedikit variasi, orang dapat membuat tabel sementara dengan hanya kolom yang akan diperbarui dan kolom referensi, menghapus kolom yang akan diperbarui dari tabel asli, lalu menggabungkan tabel menggunakan CREATE TABLE tbl_new AS SELECT t.*, u.field1, u.field2 from tbl t NATURAL LEFT JOIN tmp_tbl u;, LEFT JOINmemungkinkan untuk menjaga baris yang tidak ada pembaruan. Tentu saja NATURALdapat diubah menjadi apa pun yang valid USING()atau ON.
Skippy le Grand Gourou
2

Jika data dapat tersedia dalam file terstruktur Anda bisa membacanya dengan pembungkus data asing dan melakukan penggabungan pada tabel target.

David Aldridge
sumber
3
Apa yang Anda maksud secara khusus dengan "menggabungkan tabel target"? Mengapa menggunakan PLRT Asing lebih baik daripada menyalin ke tabel temp (seperti yang disarankan dalam butir ketiga dalam pertanyaan asli)?
Yang
"Gabungkan" seperti dalam pernyataan sql MERGE. Menggunakan FDW memungkinkan Anda untuk melakukan itu tanpa langkah tambahan menyalin data ke tabel sementara. Saya berasumsi bahwa Anda tidak mengganti seluruh kumpulan data, dan bahwa akan ada sejumlah data dalam file yang tidak akan mewakili perubahan dari kumpulan data saat ini - jika jumlah yang signifikan telah berubah maka lengkap penggantian meja mungkin bermanfaat.
David Aldridge
1
@ Davidvidridge: Meskipun didefinisikan dalam standar SQL: 2003, MERGEbelum diimplementasikan dalam PostgreSQL (belum). Implementasi dalam RDBMS lain sedikit berbeda. Pertimbangkan info tag untuk MERGEdan UPSERT.
Erwin Brandstetter
@ ErwinBrandstetter [glurk] Oh ya, cukup. Yah Gabung adalah lapisan gula pada kue benar-benar saya kira. Mengakses data tanpa langkah import-to-temporary-table-benar-benar inti dari teknik PLRT Asing.
David Aldridge