it-swarm.asia

Mengapa UPDATE Postgres membutuhkan waktu 39 jam?

Saya memiliki tabel Postgres dengan ~ 2,1 juta baris. Saya menjalankan pembaruan di bawah ini:

WITH stops AS (
    SELECT id,
           rank() OVER (ORDER BY offense_timestamp,
                     defendant_dl,
                     offense_street_number,
                     offense_street_name) AS stop
    FROM   consistent.master
    WHERE  citing_jurisdiction=1
)

UPDATE consistent.master
SET arrest_id=stops.stop
FROM stops
WHERE master.id = stops.id;

Permintaan ini membutuhkan waktu 39 jam untuk berjalan. Saya menjalankan ini pada prosesor laptop 4 (fisik) core i7 Q720, banyak RAM, tidak ada yang menjalankan sebagian besar waktu. Tidak ada kendala ruang HDD. Meja baru-baru ini telah disedot, dianalisis, dan diindeks ulang.

Sepanjang waktu kueri berjalan, setidaknya setelah WITH awal selesai, penggunaan CPU biasanya rendah, dan HDD digunakan 100%. HDD digunakan sangat keras sehingga aplikasi lain berjalan jauh lebih lambat dari biasanya.

Pengaturan daya laptop menyala Kinerja tinggi (Windows 7 x64).

Inilah yang MENJELASKAN:

Update on master  (cost=822243.22..1021456.89 rows=2060910 width=312)
  CTE stops
    ->  WindowAgg  (cost=529826.95..581349.70 rows=2060910 width=33)
          ->  Sort  (cost=529826.95..534979.23 rows=2060910 width=33)
                Sort Key: consistent.master.offense_timestamp, consistent.master.defendant_dl, consistent.master.offense_street_number, consistent.master.offense_street_name
                ->  Seq Scan on master  (cost=0.00..144630.06 rows=2060910 width=33)
                      Filter: (citing_jurisdiction = 1)
  ->  Hash Join  (cost=240893.51..440107.19 rows=2060910 width=312)
        Hash Cond: (stops.id = consistent.master.id)
        ->  CTE Scan on stops  (cost=0.00..41218.20 rows=2060910 width=48)
        ->  Hash  (cost=139413.45..139413.45 rows=2086645 width=268)
              ->  Seq Scan on master  (cost=0.00..139413.45 rows=2086645 width=268)

citing_jurisdiction=1 Hanya mengecualikan beberapa puluh ribu baris. Bahkan dengan klausa WHERE ini, saya masih beroperasi di lebih dari 2 juta baris.

Hard drive sepenuhnya dienkripsi dengan TrueCrypt 7.1a. Itu memperlambat segalanya sedikit, tetapi tidak cukup untuk menyebabkan kueri mengambil yang berjam-jam.

Bagian WITH hanya membutuhkan waktu sekitar 3 menit untuk dijalankan.

Bidang arrest_id Tidak memiliki indeks untuk kunci asing. Ada 8 indeks dan 2 kunci asing di tabel ini. Semua bidang lain dalam kueri diindeks.

Kolom arrest_id Tidak memiliki kendala kecuali NOT NULL.

Tabel ini memiliki total 32 kolom.

arrest_id Bertipe karakter bervariasi (20). Saya menyadari rank() menghasilkan nilai numerik, tetapi saya harus menggunakan karakter bervariasi (20) karena saya memiliki baris lain di mana citing_jurisdiction<>1 Yang menggunakan data non-numerik untuk lapangan ini.

Kolom arrest_id Kosong untuk semua baris dengan citing_jurisdiction=1.

Ini adalah laptop pribadi kelas atas (per 1 tahun yang lalu). Saya satu-satunya pengguna. Tidak ada pertanyaan atau operasi lain yang berjalan. Mengunci sepertinya tidak mungkin.

Tidak ada pemicu di mana pun dalam tabel ini atau di mana pun dalam database.

Operasi lain pada database ini tidak pernah memakan waktu lama. Dengan pengindeksan yang tepat, SELECT kueri biasanya cukup cepat.

17
Aren Cambre

Saya memiliki sesuatu yang serupa terjadi baru-baru ini dengan tabel 3,5 juta baris. Pembaruan saya tidak akan pernah selesai. Setelah banyak bereksperimen dan frustrasi, saya akhirnya menemukan pelakunya. Ternyata indeks di atas meja sedang diperbarui.

Solusinya adalah dengan menghapus semua indeks di atas meja yang diperbarui sebelum menjalankan pernyataan pembaruan. Setelah saya melakukan itu, pembaruan selesai dalam beberapa menit. Setelah pembaruan selesai, saya membuat kembali indeks dan kembali berbisnis. Ini mungkin tidak akan membantu Anda pada saat ini tetapi mungkin orang lain mencari jawaban.

Saya akan menyimpan indeks di atas meja tempat Anda menarik data. Yang itu tidak harus terus memperbarui indeks apa pun dan akan membantu menemukan data yang ingin Anda perbarui. Ini berjalan baik di laptop yang lambat.

19
JC Avena

Masalah terbesar Anda adalah melakukan banyak pekerjaan tulis-berat dan berat di hard drive laptop. Itu tidak akan pernah cepat, apa pun yang Anda lakukan, terutama jika drive jenis 5400RPM yang lebih lambat dikirim dalam banyak laptop.

TrueCrypt memperlambat segalanya lebih dari "sedikit" untuk menulis. Membaca akan cukup cepat, tetapi menulis membuat RAID 5 terlihat cepat. Menjalankan DB pada volume TrueCrypt akan menjadi siksaan untuk menulis, terutama menulis acak.

Dalam hal ini, saya pikir Anda akan membuang-buang waktu untuk mencoba mengoptimalkan kueri. Anda menulis ulang sebagian besar baris, dan itu akan menjadi lambat dengan situasi penulisan Anda yang mengerikan. Apa yang saya sarankan adalah:

BEGIN;
SELECT ... INTO TEMPORARY TABLE master_tmp ;
TRUNCATE TABLE consistent.master;
-- Now DROP all constraints on consistent.master, then:
INSERT INTO consistent.master SELECT * FROM master_tmp;
-- ... and re-create any constraints.

Saya menduga itu akan lebih cepat daripada hanya menjatuhkan dan menciptakan kembali kendala saja, karena UPDATE akan memiliki pola penulisan yang cukup acak yang akan membunuh Anda penyimpanan. Dua sisipan massal, satu ke dalam tabel tanpa log dan satu ke dalam tabel log-WAL tanpa kendala, mungkin akan lebih cepat.

Jika Anda memiliki cadangan yang benar-benar terkini dan tidak keberatan harus memulihkan database Anda dari cadangan Anda juga dapat memulai kembali PostgreSQL dengan fsync=off parameter dan full_page_writes=off sementara untuk operasi massal ini. Masalah tak terduga seperti kehilangan daya atau kerusakan OS akan membuat basis data Anda tidak dapat dipulihkan sementara fsync=off.

POSTGreSQL setara dengan "tidak ada penebangan" adalah dengan menggunakan tabel yang tidak di-log. Tabel-tabel yang tidak di-log-kan ini terpotong jika DB dimatikan dengan tidak bersih saat mereka kotor. Menggunakan tabel yang tidak terdaftar setidaknya akan mengurangi separuh beban tulis Anda dan mengurangi jumlah pencarian, sehingga mereka bisa menjadi LOT lebih cepat.

Seperti di Oracle, ini bisa menjadi ide yang baik untuk menjatuhkan indeks kemudian membuatnya kembali setelah pembaruan batch besar. Perencana PostgreSQL tidak dapat menemukan bahwa pembaruan besar sedang berlangsung, jeda pembaruan indeks, kemudian buat kembali indeks di akhir; bahkan jika itu bisa, akan sangat sulit baginya untuk mengetahui pada titik mana hal ini layak dilakukan, terutama di muka.

15
Craig Ringer

Seseorang akan memberikan jawaban yang lebih baik untuk Postgres, tetapi di sini ada beberapa pengamatan dari perspektif Oracle yang mungkin berlaku (dan komentarnya terlalu panjang untuk kolom komentar).

Kekhawatiran pertama saya adalah mencoba memperbarui 2 juta baris dalam satu transaksi. Di Oracle, Anda akan menulis gambar sebelum setiap blok yang diperbarui sehingga sesi lain masih memiliki pembacaan yang konsisten tanpa membaca blok yang dimodifikasi dan Anda memiliki kemampuan untuk mengembalikan. Itu adalah kemunduran yang lama sedang dibangun. Anda biasanya lebih baik melakukan transaksi dalam potongan kecil. Katakan 1.000 catatan sekaligus.

Jika Anda memiliki indeks di atas meja, dan tabel tersebut akan dianggap tidak beroperasi selama pemeliharaan, Anda seringkali lebih baik menghapus indeks sebelum operasi besar dan kemudian membuatnya kembali setelahnya. Lebih murah kemudian terus berusaha mempertahankan indeks dengan setiap catatan yang diperbarui.

Oracle mengizinkan "no logging" petunjuk pada pernyataan untuk menghentikan penjurnalan. Ini mempercepat pernyataan banyak, tetapi meninggalkan db Anda dalam situasi "tidak dapat dipulihkan". Jadi, Anda ingin mencadangkan sebelumnya, dan mencadangkan lagi segera sesudahnya. Saya tidak tahu apakah Postgres memiliki opsi serupa.

2
Glenn