Terkadang, database PostgreSQL perlu mengimpor data dalam jumlah besar dalam satu atau beberapa langkah. Ini umumnya dikenal sebagai impor data massal di mana sumber data biasanya satu atau beberapa file besar. Proses ini terkadang sangat lambat.
Ada banyak alasan untuk kinerja yang buruk:indeks, pemicu, kunci asing, kunci utama GUID, atau bahkan Write Ahead Log (WAL) semuanya dapat menyebabkan penundaan.
Pada artikel ini, kami akan membahas beberapa tip praktik terbaik untuk mengimpor data secara massal ke database PostgreSQL. Namun, mungkin ada situasi di mana tidak satu pun dari tips ini akan menjadi solusi yang efisien. Kami menyarankan pembaca untuk mempertimbangkan pro dan kontra dari metode apa pun sebelum menerapkannya.
Kiat 1:Ubah Tabel Target ke Mode Tidak Tercatat
Untuk PostgreSQL 9.5 dan yang lebih baru, tabel target dapat diubah terlebih dahulu menjadi UNLOGGED, kemudian diubah kembali menjadi LOGGED setelah data dimuat:
ALTER TABLE <target table> SET UNLOGGED
<bulk data insert operations…>
ALTER TABLE <target table> LOGGED
Mode UNLOGGED memastikan PostgreSQL tidak mengirim operasi penulisan tabel ke Write Ahead Log (WAL). Ini dapat membuat proses pemuatan menjadi sangat cepat. Namun, karena operasi tidak dicatat, data tidak dapat dipulihkan jika terjadi crash atau shutdown server yang tidak bersih selama pemuatan. PostgreSQL akan secara otomatis memotong tabel yang tidak masuk log setelah dimulai ulang.
Juga, tabel yang tidak dicatat tidak direplikasi ke server siaga. Dalam kasus seperti itu, ulangan yang ada harus dihapus sebelum memuat dan dibuat kembali setelah dimuat. Bergantung pada volume data di node utama dan jumlah siaga, waktu untuk membuat ulang replikasi mungkin cukup lama, dan tidak dapat diterima oleh persyaratan ketersediaan tinggi.
Kami merekomendasikan praktik terbaik berikut untuk menyisipkan data secara massal ke dalam tabel yang tidak masuk log:
- Membuat cadangan tabel dan data sebelum mengubahnya ke mode tidak masuk log
- Membuat ulang replikasi apa pun ke server siaga setelah pemuatan data selesai
- Menggunakan sisipan massal yang tidak dicatat untuk tabel yang dapat dengan mudah diisi ulang (misalnya tabel pencarian besar atau tabel dimensi)
Kiat 2:Jatuhkan dan Buat Ulang Indeks
Indeks yang ada dapat menyebabkan penundaan yang signifikan selama penyisipan data massal. Ini karena setiap baris ditambahkan, entri indeks yang sesuai juga harus diperbarui.
Kami merekomendasikan untuk menjatuhkan indeks di tabel target jika memungkinkan sebelum memulai penyisipan massal, dan membuat ulang indeks setelah pemuatan selesai. Sekali lagi, membuat indeks pada tabel besar dapat memakan waktu, tetapi umumnya akan lebih cepat daripada memperbarui indeks selama pemuatan.
DROP INDEX <index_name1>, <index_name2> … <index_name_n>
<bulk data insert operations…>
CREATE INDEX <index_name> ON <target_table>(column1, …,column n)
Mungkin bermanfaat untuk sementara meningkatkan maintenance_work_mem parameter konfigurasi sebelum membuat indeks. Peningkatan memori kerja dapat membantu membuat indeks lebih cepat.
Pilihan lain untuk bermain aman adalah membuat salinan tabel target di database yang sama dengan data dan indeks yang ada. Tabel yang baru disalin ini kemudian dapat diuji dengan penyisipan massal untuk kedua skenario:indeks jatuhkan dan buat ulang, atau perbarui secara dinamis. Metode yang menghasilkan kinerja yang lebih baik dapat diikuti untuk tabel langsung.
Kiat 3:Lepaskan dan Buat Ulang Kunci Asing
Seperti indeks, batasan kunci asing juga dapat memengaruhi kinerja pemuatan massal. Ini karena setiap kunci asing di setiap baris yang disisipkan harus diperiksa keberadaan kunci utama yang sesuai. Di belakang layar, PostgreSQL menggunakan pemicu untuk melakukan pemeriksaan. Saat memuat banyak baris, pemicu ini harus diaktifkan untuk setiap baris, menambah overhead.
Kecuali dibatasi oleh aturan bisnis, kami sarankan untuk menghapus semua kunci asing dari tabel target, memuat data dalam satu transaksi, lalu membuat ulang kunci asing setelah melakukan transaksi.
ALTER TABLE <target_table>
DROP CONSTRAINT <foreign_key_constraint>
BEGIN TRANSACTION
<bulk data insert operations…>
COMMIT
ALTER TABLE <target_table>
ADD CONSTRAINT <foreign key constraint>
FOREIGN KEY (<foreign_key_field>)
REFERENCES <parent_table>(<primary key field>)...
Sekali lagi, tingkatkan maintenance_work_mem parameter konfigurasi dapat meningkatkan kinerja pembuatan ulang batasan kunci asing.
Kiat 4:Nonaktifkan Pemicu
Pemicu INSERT atau DELETE (jika proses pemuatan juga melibatkan penghapusan catatan dari tabel target) dapat menyebabkan penundaan dalam pemuatan data massal. Ini karena setiap pemicu akan memiliki logika yang perlu diperiksa dan operasi yang perlu diselesaikan tepat setelah setiap baris DIMASUKKAN atau DIHAPUS.
Sebaiknya nonaktifkan semua pemicu di tabel target sebelum memuat data secara massal dan aktifkan setelah pemuatan selesai. Menonaktifkan SEMUA pemicu juga menyertakan pemicu sistem yang memberlakukan pemeriksaan batasan kunci asing.
ALTER TABLE <target table> DISABLE TRIGGER ALL
<bulk data insert operations…>
ALTER TABLE <target table> ENABLE TRIGGER ALL
Kiat 5:Gunakan Perintah SALIN
Kami merekomendasikan penggunaan PostgreSQL COPY perintah untuk memuat data dari satu atau lebih file. SALIN dioptimalkan untuk memuat data massal. Ini lebih efisien daripada menjalankan sejumlah besar pernyataan INSERT atau bahkan INSERT multi-nilai.
COPY <target table> [( column1>, … , <column_n>)]
FROM '<file_name_and_path>'
WITH (<option1>, <option2>, … , <option_n>)
Manfaat lain menggunakan COPY meliputi:
- Ini mendukung impor file teks dan biner
- Ini bersifat transaksional
- Ini memungkinkan menentukan struktur file input
- Ini dapat memuat data secara kondisional menggunakan klausa WHERE
Kiat 6:Gunakan INSERT Multinilai
Menjalankan beberapa ribu atau beberapa ratus ribu pernyataan INSERT bisa menjadi pilihan yang buruk untuk memuat data massal. Itu karena setiap perintah INSERT individu harus diuraikan dan disiapkan oleh pengoptimal kueri, melalui semua pemeriksaan batasan, dijalankan sebagai transaksi terpisah, dan masuk ke WAL. Menggunakan pernyataan INSERT tunggal multi-nilai dapat menghemat overhead ini.
INSERT INTO <target_table> (<column1>, <column2>, …, <column_n>)
VALUES
(<value a>, <value b>, …, <value x>),
(<value 1>, <value 2>, …, <value n>),
(<value A>, <value B>, …, <value Z>),
(<value i>, <value ii>, …, <value L>),
...
Kinerja INSERT multi-nilai dipengaruhi oleh indeks yang ada. Kami merekomendasikan untuk menghapus indeks sebelum menjalankan perintah dan membuat ulang indeks setelahnya.
Area lain yang harus diperhatikan adalah jumlah memori yang tersedia untuk PostgreSQL untuk menjalankan INSERT multi-nilai. Ketika INSERT multi-nilai dijalankan, sejumlah besar nilai input harus masuk ke dalam RAM, dan kecuali ada cukup memori yang tersedia, prosesnya mungkin gagal.
Sebaiknya atur efektif_cache_size parameter menjadi 50%, dan shared_buffer parameter hingga 25% dari total RAM mesin. Selain itu, agar aman, ia menjalankan serangkaian INSERT multi-nilai dengan setiap pernyataan memiliki nilai untuk 1000 baris.
Kiat 7:Jalankan ANALYZE
Ini tidak terkait dengan peningkatan kinerja impor data massal, tetapi kami sangat menyarankan untuk menjalankan ANALISIS perintah pada tabel target segera setelah impor massal. Sejumlah besar baris baru akan secara signifikan mengubah distribusi data dalam kolom dan akan menyebabkan statistik yang ada pada tabel menjadi usang. Saat pengoptimal kueri menggunakan statistik basi, kinerja kueri bisa sangat buruk. Menjalankan perintah ANALYZE akan memastikan semua statistik yang ada diperbarui.
Pemikiran Terakhir
Impor data massal mungkin tidak terjadi setiap hari untuk aplikasi database, tetapi ada dampak kinerja pada kueri saat dijalankan. Itulah mengapa perlu untuk meminimalkan waktu buka sebaik mungkin. Satu hal yang dapat dilakukan DBA untuk meminimalkan kejutan adalah dengan menguji pengoptimalan beban dalam lingkungan pengembangan atau staging dengan spesifikasi server dan konfigurasi PostgreSQL yang serupa. Setiap skenario pemuatan data berbeda, dan yang terbaik adalah mencoba setiap metode dan menemukan yang berhasil.