Bagaimana cara menyalin jutaan baris secara efisien dari satu tabel ke tabel lain di Postgresql?

37

Saya punya dua tabel database. Satu berisi ratusan juta catatan. Mari kita panggil yang itu history. Yang lain dihitung setiap hari dan saya ingin menyalin semua catatannya menjadi historysatu.

Apa yang saya lakukan adalah menjalankan:

INSERT INTO history SELECT * FROM daily

Dan itu melakukan trik untuk sementara waktu, tetapi mulai menjadi lebih lambat dan lebih lambat karena jumlah catatan terus bertambah. Sekarang saya memiliki sekitar 2 juta catatan yang perlu disalin dari dailyke historydalam operasi tunggal dan butuh waktu terlalu lama untuk diselesaikan.

Apakah ada cara lain yang lebih efisien untuk menyalin data dari satu tabel ke tabel lainnya?

Milovan Zogovic
sumber

Jawaban:

10

Jika Anda berencana untuk menyimpan sejarah untuk jangka waktu yang lama (berbulan-bulan), saya sarankan melihat opsi pemartisian - mungkin satu partisi untuk setiap hari atau minggu dan seterusnya. Itu tergantung pada pola akses tabel histori Anda juga (apakah Anda menjalankan kueri yang mengakses data lintas tanggal? Apakah Anda melakukan banyak agregasi, dll). Lihatlah tampilan terwujud untuk menyimpan agregat / ringkasan. http://www.postgresql.org/docs/9.3/static/ddl-partitioning.html http://www.postgresql.org/docs/9.3/static/sql-creatematerializedview.html

Jayadevan
sumber
Terima kasih atas jawabannya. Sepertinya satu-satunya cara untuk pergi. Saya perlu mempartisi data berdasarkan bulan dan dengan demikian membuat pengindeksan ulang (karena regenerasi indeks adalah masalah di sini) jauh lebih cepat.
Milovan Zogovic
16

Buang tabel dalam format csv

COPY table TO '/tmp/table.csv' DELIMITER ',';

gunakan perintah COPY yang jauh lebih efisien untuk sejumlah besar data.

COPY table FROM '/tmp/table.csv' DELIMITER ',';

Periksa postgres docs di http://www.postgresql.org/docs/current/static/sql-copy.html untuk info lebih lanjut

Fabrizio Mazzoni
sumber
1
Masih berjalan sangat, sangat lambat ... Mungkin harus melakukan sesuatu dengan harus membangun kembali indeks sebesar itu? Ada 160 juta baris dalam historytabel, dan kami menambahkan 3 juta baris lagi.
Milovan Zogovic
2
Jika Anda mengisi tabel kosong, atau menambahkan lebih banyak baris dari yang sudah ada, biasanya lebih efisien untuk menjatuhkan indeks yang tidak berkerumun dan membuatnya kembali setelah transfer selesai (kecuali jika ada penggunaan aktif dari tabel pada saat itu) )
David Spillett
BTW, apakah ini operasi satu kali atau itu sesuatu yang harus Anda lakukan secara teratur? Jika itu secara teratur saya menyarankan Anda membuat pemicu sehingga Anda tidak harus melalui cobaan ini setiap kali.
Fabrizio Mazzoni
@FabrizioMazzoni - Itu harus dilakukan setiap hari pada waktu tertentu (agak mengambil foto dalam waktu).
Milovan Zogovic
@ David Spillett - memang! Menjatuhkan indeks membuat impor sangat cepat (lihat jawaban saya di atas), namun, membuat ulang indeks membutuhkan waktu berjam-jam (karena saya memiliki
160 juta
14

Masalahnya dengan indeks. The historymeja memiliki 160M diindeks baris. Dengan menjalankan salah satu COPY FROMatau INSERT INTO .. SELECTbutuh banyak waktu untuk tidak memasukkan baris, tetapi untuk memperbarui indeks. Ketika saya menonaktifkan indeks, itu mengimpor baris 3M dalam 10 detik. Sekarang saya perlu menemukan cara yang lebih cepat untuk mengindeks ulang tabel besar.

Milovan Zogovic
sumber
3
Apakah Anda bahkan perlu indeks pada tabel riwayat?
Sherlock
2
Tambahkan indeks menggunakan kata kunci CONCURRENTLY
Akvel
11

Anda dapat menggunakan alat psql , saya mungkin efisien, sebagai berikut,

psql -h ${DAILY_HOST_IP} -p ${PG_PORT} ${DB_NAME} ${USER_NAME} -c "copy daily to stdout " | psql -h ${HISTORY_HOST_IP} -p ${PG_PORT} ${DB_NAME} ${USER_NAME}  -c "copy history from stdin"

Anda juga dapat menulis skrip shell.

franc
sumber
Solusi hebat tanpa file perantara. Sangat cepat juga, saya menyalin tabel baris 950 juta dalam 1 jam 20 (tanpa indeks) antara disk biasa dan sistem file jaringan.
Le Droid
Sayang sekali hal ini tidak dapat dilakukan secara langsung dari satu meja ke meja lainnya.
Charlie Clark
3

Ini tentu saja bukan jawaban yang tepat untuk pertanyaan Anda, tetapi jika Anda tidak perlu mengakses historytabel, Anda juga bisa menghasilkan SQL dump:

pg_dump -h host -p port -w -U user db > dump.sql

Maka orang dapat menggunakan alat seperti gituntuk menghitung perbedaan dan menyimpan ini secara efisien.

git add dump.sql
git commit -m "temp dump"
git gc --aggressive

Ini berguna karena sebagian besar dalam database, tidak akan berubah setiap hari. Alih-alih menyimpan seluruh salinan untuk setiap hari, orang dapat menyimpan perbedaan antara dua hari.

Anda dapat menggunakan crontabpekerjaan sedemikian rupa sehingga dump diproses setiap hari.

Willem Van Onsem
sumber