Saya memiliki database PostgreSQL 9.2 yang memiliki skema utama dengan sekitar 70 tabel dan sejumlah variabel skema per-klien yang terstruktur identik masing-masing 30 tabel. Skema klien memiliki kunci asing yang merujuk pada skema utama dan bukan sebaliknya.
Saya baru saja mulai mengisi database dengan beberapa data nyata yang diambil dari versi sebelumnya. DB telah mencapai sekitar 1,5 GB (diharapkan tumbuh beberapa GB 10s dalam beberapa minggu) ketika saya harus melakukan penghapusan massal di tabel yang sangat sentral dalam skema utama. Semua kunci asing yang bersangkutan ditandai ON DELETE CASCADE.
Tidak mengherankan bahwa ini akan memakan waktu lama tetapi setelah 12 jam menjadi jelas bahwa saya lebih baik memulai dari awal, menjatuhkan DB dan meluncurkan migrasi lagi. Tetapi bagaimana jika saya perlu mengulangi operasi ini nanti ketika DB hidup dan jauh lebih besar? Adakah metode alternatif dan lebih cepat?
Apakah akan jauh lebih cepat jika saya menulis skrip yang akan menelusuri tabel dependen, mulai dari tabel terjauh dari tabel pusat, menghapus tabel baris dependen demi tabel?
Detail penting adalah bahwa ada pemicu pada beberapa tabel.
Jawaban:
Saya punya masalah serupa. Ternyata,
ON DELETE CASCADE
pemicu itu memperlambat sedikit, karena penghapusan bertingkat sangat lambat.Saya memecahkan masalah dengan membuat indeks pada bidang kunci asing pada tabel referensi, dan saya beralih dari mengambil banyak jam untuk penghapusan menjadi beberapa detik.
sumber
ON DELETE CASCADE
)EXPLAIN (ANALYZE, BUFFERS)
kueri pada satu baris penghapusan dan itu akan menunjukkan kepada Anda batasan kunci asing mana yang paling lama (paling tidak bagi saya).PRIMARY
indeks cukup tetapiUNIQUE
indeks jelas tidak cukup baik untuk tujuan ini.Anda punya beberapa pilihan. Opsi terbaik adalah menjalankan penghapusan batch sehingga pemicu tidak terkena. Nonaktifkan pemicu sebelum menghapus, lalu aktifkan kembali. Ini menghemat waktu Anda yang sangat besar. Sebagai contoh:
Kunci utama di sini adalah Anda ingin meminimalkan kedalaman subquery. Dalam hal ini Anda mungkin ingin mengatur tabel temp untuk menyimpan informasi yang relevan sehingga Anda dapat menghindari subkueri yang dalam pada penghapusan Anda.
sumber
Metode termudah untuk memecahkan masalah adalah untuk query waktu rinci dari PostgreSQL:
EXPLAIN
. Untuk ini, Anda perlu menemukan setidaknya satu permintaan yang lengkap tetapi membutuhkan waktu lebih lama dari yang diharapkan. Katakanlah garis ini akan terlihat sepertiAlih-alih benar-benar menjalankan perintah itu bisa Anda lakukan
Kembalikan pada akhirnya memungkinkan menjalankan ini tanpa benar-benar memodifikasi database tetapi Anda masih mendapatkan waktu yang terperinci dari berapa banyak. Setelah menjalankan itu, Anda mungkin menemukan dalam output bahwa beberapa pemicu menyebabkan penundaan besar:
Ini
time
dalam ms (milidetik) sehingga memeriksa kendala ini memakan waktu sekitar 12,3 detik. Anda perlu menambahkan yang baru diINDEX
atas kolom yang diperlukan sehingga pemicu ini dapat dihitung secara efektif. Untuk referensi kunci asing, kolom yang merujuk ke tabel lain harus diindeks (yaitu, kolom sumber, bukan kolom target). PostgreSQL tidak secara otomatis membuat indeks untuk Anda danDELETE
merupakan satu-satunya permintaan umum di mana Anda benar-benar membutuhkan indeks itu. Akibatnya, Anda mungkin telah mengakumulasikan data selama bertahun-tahun hingga Anda menemukan kasusDELETE
yang terlalu lambat karena tidak ada indeks.Setelah Anda memperbaiki kinerja kendala itu (atau hal lain yang memakan waktu terlalu lama), ulangi perintah di
begin
/rollback
block sehingga Anda dapat membandingkan waktu eksekusi yang baru dengan yang sebelumnya. Lanjutkan sampai Anda puas dengan waktu respons penghapusan satu baris (saya mendapatkan satu kueri mulai dari 25,6 detik hingga 15 ms hanya dengan menambahkan indeks yang berbeda). Kemudian Anda dapat melanjutkan untuk menyelesaikan penghapusan penuh Anda tanpa ada peretasan.(Catatan yang
EXPLAIN
membutuhkan kueri yang dapat diselesaikan dengan sukses. Saya pernah punya masalah di mana PostgreSQL butuh waktu terlalu lama untuk mencari tahu bahwa satu penghapusan akan melanggar batasan kunci asing dan dalam kasus ituEXPLAIN
tidak dapat digunakan karena tidak akan memancarkan waktu untuk gagal pertanyaan. Saya tidak tahu cara mudah untuk men-debug masalah kinerja dalam kasus seperti itu.)sumber
Menonaktifkan pemicu dapat menjadi ancaman bagi integritas DB dan tidak dapat direkomendasikan; namun jika Anda yakin operasi Anda kendala-kegagalan-bukti, Anda dapat menonaktifkan pemicu, dengan berikut:
SET session_replication_role = replica;
Jalankan
DELETE
sini.Untuk mengembalikan pemicu, jalankan:
SET session_replication_role = DEFAULT;
Sumber di sini.
sumber
Jika Anda memiliki pemicu ON DELETE CASCADE, mereka mudah-mudahan ada karena suatu alasan, dan karenanya tidak boleh dinonaktifkan. Trik lain (masih menambahkan indeks Anda) yang berfungsi untuk saya adalah membuat fungsi hapus yang secara manual menghapus data yang dimulai dengan tabel di akhir kaskade, dan bekerja menuju tabel utama. (Ini sama dengan yang harus Anda lakukan jika Anda memiliki pemicu ON DELETE RESTRICT)
Dalam hal ini hapus data dalam tablec lalu tableb lalu tablea
sumber