Saya sedang menguji kinerja penyisipan Postgres. Saya punya tabel dengan satu kolom dengan nomor sebagai tipe datanya. Ada juga indeksnya. Saya mengisi basis data menggunakan kueri ini:
insert into aNumber (id) values (564),(43536),(34560) ...
Saya memasukkan 4 juta baris dengan sangat cepat 10.000 sekaligus dengan permintaan di atas. Setelah database mencapai 6 juta baris, kinerja menurun drastis menjadi 1 Juta baris setiap 15 menit. Apakah ada trik untuk meningkatkan kinerja penyisipan? Saya membutuhkan kinerja penyisipan yang optimal pada proyek ini.
Menggunakan Windows 7 Pro pada mesin dengan RAM 5 GB.
sql
postgresql
bulkinsert
sql-insert
Luke101
sumber
sumber
Jawaban:
Lihat mengisi database dalam manual PostgreSQL, artikel bagus seperti biasa pada depesz , dan pertanyaan SO ini .
(Perhatikan bahwa jawaban ini adalah tentang memuat data secara massal ke dalam DB yang ada atau membuat yang baru. Jika Anda tertarik DB memulihkan kinerja dengan
pg_restore
ataupsql
mengeksekusipg_dump
output, banyak dari ini tidak berlaku sejakpg_dump
danpg_restore
sudah melakukan hal-hal seperti membuat pemicu dan indeks setelah selesai skema + pemulihan data) .Banyak yang harus dilakukan. Solusi yang ideal adalah mengimpor ke
UNLOGGED
tabel tanpa indeks, kemudian mengubahnya menjadi login dan menambahkan indeks. Sayangnya di PostgreSQL 9.4 tidak ada dukungan untuk mengubah tabel dariUNLOGGED
menjadi login. 9.5 menambahkanALTER TABLE ... SET LOGGED
untuk mengizinkan Anda melakukan ini.Jika Anda dapat membuat database Anda offline untuk impor massal, gunakan
pg_bulkload
.Jika tidak:
Nonaktifkan semua pemicu di atas meja
Jatuhkan indeks sebelum memulai impor, buat kembali setelahnya. (Dibutuhkan jauh lebih sedikit waktu untuk membangun indeks dalam satu pass daripada untuk menambahkan data yang sama secara progresif, dan indeks yang dihasilkan jauh lebih kompak).
Jika melakukan impor dalam satu transaksi, aman untuk menghilangkan batasan kunci asing, melakukan impor, dan menciptakan kembali kendala sebelum melakukan. Jangan lakukan ini jika impor dibagi menjadi beberapa transaksi karena Anda mungkin memasukkan data yang tidak valid.
Jika memungkinkan, gunakan
COPY
sebagai gantiINSERT
sJika Anda tidak dapat menggunakan,
COPY
pertimbangkan untuk menggunakan multi-nilaiINSERT
jika praktis. Anda sepertinya sudah melakukan ini. Jangan mencoba mendaftar terlalu banyak nilai dalam satuVALUES
; nilai-nilai itu harus sesuai dengan memori beberapa kali lipat, jadi simpan beberapa ratus per pernyataan.Batch insert Anda ke dalam transaksi eksplisit, lakukan ratusan ribu atau jutaan insert per transaksi. Tidak ada batasan praktis AFAIK, tetapi batching akan memungkinkan Anda pulih dari kesalahan dengan menandai awal setiap batch dalam data input Anda. Sekali lagi, Anda tampaknya sudah melakukan ini.
Gunakan
synchronous_commit=off
dan besarcommit_delay
untuk mengurangi biaya fsync (). Ini tidak akan banyak membantu jika Anda telah memasukkan pekerjaan Anda ke dalam transaksi besar.INSERT
atauCOPY
secara paralel dari beberapa koneksi. Berapa banyak tergantung pada subsistem disk perangkat keras Anda; Sebagai aturan praktis, Anda ingin satu koneksi per hard drive fisik jika menggunakan penyimpanan yang terpasang langsung.Tetapkan
checkpoint_segments
nilai tinggi dan aktifkanlog_checkpoints
. Lihatlah log PostgreSQL dan pastikan tidak mengeluh tentang pos pemeriksaan yang terjadi terlalu sering.Jika dan hanya jika Anda tidak keberatan kehilangan seluruh cluster PostgreSQL Anda (database Anda dan yang lainnya pada cluster yang sama) menjadi bencana besar jika sistem crash saat impor, Anda dapat menghentikan Pg, mengatur
fsync=off
, memulai Pg, melakukan impor, lalu (sangat) hentikan Pg dan aturfsync=on
lagi. Lihat konfigurasi WAL . Jangan lakukan ini jika sudah ada data yang Anda pedulikan di basis data apa pun di instalasi PostgreSQL Anda. Jika Anda mengaturfsync=off
Anda juga dapat mengaturfull_page_writes=off
; sekali lagi, ingatlah untuk mengaktifkannya kembali setelah impor Anda untuk mencegah korupsi basis data dan kehilangan data. Lihat pengaturan yang tidak tahan lama dalam manual Pg.Anda juga harus melihat penyetelan sistem Anda:
Gunakan SSD berkualitas baik untuk penyimpanan sebanyak mungkin. SSD yang baik dengan cache write-back yang andal dan dilindungi daya membuat tingkat komit jauh lebih cepat. Mereka kurang bermanfaat ketika Anda mengikuti saran di atas - yang mengurangi flush disk / jumlah
fsync()
s - tetapi masih bisa sangat membantu. Jangan gunakan SSD murah tanpa perlindungan kegagalan daya yang benar kecuali Anda tidak peduli untuk menyimpan data Anda.Jika Anda menggunakan RAID 5 atau RAID 6 untuk penyimpanan yang terhubung langsung, hentikan sekarang. Cadangkan data Anda, merestrukturisasi array RAID Anda ke RAID 10, dan coba lagi. RAID 5/6 tidak memiliki harapan untuk kinerja penulisan massal - meskipun pengontrol RAID yang baik dengan cache yang besar dapat membantu.
Jika Anda memiliki opsi untuk menggunakan pengontrol RAID perangkat keras dengan cache write-back yang didukung baterai besar ini benar-benar dapat meningkatkan kinerja penulisan untuk beban kerja dengan banyak komitmen. Tidak banyak membantu jika Anda menggunakan komit async dengan commit_delay atau jika Anda melakukan lebih sedikit transaksi besar selama pemuatan massal.
Jika memungkinkan, simpan WAL (
pg_xlog
) pada disk / array disk yang terpisah. Ada sedikit gunanya menggunakan sistem file terpisah pada disk yang sama. Orang sering memilih untuk menggunakan pasangan RAID1 untuk WAL. Sekali lagi, ini memiliki lebih banyak efek pada sistem dengan tingkat komit yang tinggi, dan itu memiliki sedikit efek jika Anda menggunakan tabel yang tidak dicatat sebagai target pemuatan data.Anda juga mungkin tertarik dalam Optimasi PostgreSQL untuk pengujian cepat .
sumber
indisvalid
( postgresql.org/docs/8.3/static/catalog-pg-index.html ) ke false, lalu memuat data dan kemudian membawa indeks secara onlineREINDEX
?UNLOGGED
? Tes cepat menunjukkan peningkatan 10-20%.Penggunaan
COPY table TO ... WITH BINARY
yang sesuai dengan dokumentasi adalah " agak lebih cepat daripada format teks dan CSV ." Hanya lakukan ini jika Anda memiliki jutaan baris untuk disisipkan, dan jika Anda merasa nyaman dengan data biner.Berikut adalah contoh resep dalam Python, menggunakan psycopg2 dengan input biner .
sumber
Selain posting Craig Ringer dan posting blog depesz yang luar biasa, jika Anda ingin mempercepat sisipan Anda melalui antarmuka ODBC ( psqlodbc ) dengan menggunakan sisipan pernyataan siap pakai dalam transaksi, ada beberapa hal tambahan yang perlu Anda lakukan untuk membuatnya bekerja cepat:
Protocol=-1
dalam string koneksi. Secara default psqlodbc menggunakan level "Pernyataan", yang menciptakan SAVEPOINT untuk setiap pernyataan dan bukannya seluruh transaksi, membuat sisipan lebih lambat.UseServerSidePrepare=1
dalam string koneksi. Tanpa opsi ini, klien mengirim seluruh pernyataan penyisipan beserta setiap baris yang dimasukkan.SQLSetConnectAttr(conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>(SQL_AUTOCOMMIT_OFF), 0);
SQLEndTran(SQL_HANDLE_DBC, conn, SQL_COMMIT);
. Tidak perlu secara eksplisit membuka transaksi.Sayangnya, psqlodbc "mengimplementasikan"
SQLBulkOperations
dengan mengeluarkan serangkaian pernyataan sisipan yang tidak siap, sehingga untuk mencapai insert tercepat, seseorang perlu mengkodekan langkah-langkah di atas secara manual.sumber
A8=30000000
dalam string koneksi juga harus digunakan untuk mempercepat pemasangan.Saya menghabiskan sekitar 6 jam untuk masalah yang sama hari ini. Sisipan berjalan pada kecepatan 'reguler' (kurang dari 3detik per 100K) hingga 5MI (dari total 30MI) baris dan kemudian kinerja tenggelam secara drastis (hingga 1 menit per 100K).
Saya tidak akan mencantumkan semua hal yang tidak berhasil dan memotong langsung ke daging.
Saya menjatuhkan kunci utama pada tabel target (yang merupakan GUID) dan 30MI atau baris saya dengan gembira mengalir ke tujuan mereka dengan kecepatan konstan kurang dari 3detik per 100K.
sumber
Jika Anda happend untuk memasukkan colums dengan UUIDs (yang tidak persis kasus Anda) dan untuk menambah @Dennis jawaban (aku tidak bisa komentar belum), menjadi nasihat daripada menggunakan gen_random_uuid () (membutuhkan PG 9.4 dan modul pgcrypto) adalah (a banyak) lebih cepat dari uuid_generate_v4 ()
vs.
Juga, ini adalah cara resmi yang disarankan untuk melakukannya
Ini memasukkan waktu memasukkan dari ~ 2 jam hingga ~ 10 menit untuk 3,7M baris.
sumber
Untuk kinerja Penyisipan yang optimal, nonaktifkan indeks jika itu opsi untuk Anda. Selain itu, perangkat keras yang lebih baik (disk, memori) juga bermanfaat
sumber
Saya mengalami masalah kinerja penyisipan ini juga. Solusi saya adalah menelurkan beberapa rutinitas go untuk menyelesaikan pekerjaan penyisipan. Sementara itu,
SetMaxOpenConns
harus diberi nomor yang benar jika tidak terlalu banyak kesalahan koneksi terbuka akan diperingatkan.Kecepatan memuat jauh lebih cepat untuk proyek saya. Cuplikan kode ini hanya memberi gambaran bagaimana cara kerjanya. Pembaca harus dapat memodifikasinya dengan mudah.
sumber