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 awal WITH
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 berada pada 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 WHERE
klausa itu, saya masih beroperasi di lebih dari 2 juta baris.
Hard drive sepenuhnya dienkripsi dengan TrueCrypt 7.1a. Yang memperlambat segalanya sedikit, tetapi tidak cukup untuk menyebabkan query untuk mengambil yang berjam-jam.
Bagian ini WITH
hanya membutuhkan waktu sekitar 3 menit untuk dijalankan.
The arrest_id
lapangan tidak indeks untuk kunci asing. Ada 8 indeks dan 2 kunci asing di tabel ini. Semua bidang lain dalam kueri diindeks.
The arrest_id
lapangan tidak memiliki kendala kecuali NOT NULL
.
Tabel ini memiliki total 32 kolom.
arrest_id
adalah tipe karakter yang bervariasi (20) . Saya menyadari rank()
menghasilkan nilai numerik, tetapi saya harus menggunakan karakter yang bervariasi (20) karena saya memiliki baris lain di mana citing_jurisdiction<>1
yang menggunakan data non-numerik untuk bidang ini.
The arrest_id
bidang itu 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
permintaan biasanya cukup cepat.
sumber
Seq Scan
agak menakutkan ...Jawaban:
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.
sumber
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 lambat dengan situasi penulisan Anda yang mengerikan. Apa yang saya sarankan adalah:
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 penyimpanan Anda. 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 mutakhir dan tidak keberatan harus memulihkan database Anda dari cadangan, Anda juga dapat memulai kembali PostgreSQL dengan
fsync=off
parameter dan untukfull_page_writes=off
sementara waktu untuk operasi massal ini. Setiap masalah tak terduga seperti kehilangan daya atau OS crash akan membuat basis data Anda tidak dapat dipulihkanfsync=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 penulisan Anda dan mengurangi jumlah pencarian, sehingga mereka bisa menjadi BANYAK 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 terjadi, 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.
sumber
HOT
itu maka lebih baik membiarkan indeks di tempat. Jika tidak, maka Anda mungkin ingin melepaskan dan membuat kembali. Kolom tidak diindeks, tetapi untuk dapat melakukan pembaruan HOT di sana juga perlu ada ruang kosong di halaman yang sama, jadi itu tergantung sedikit pada seberapa banyak ruang kosong yang ada di tabel. Jika kebanyakan menulis, saya akan mengatakan drop all indexes. Jika banyak diperbarui, mungkin ada lubang dan Anda mungkin OK. Alat-alat sukapageinspect
danpg_freespacemap
dapat membantu menentukan ini.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.
sumber
VACUUM
hanya setengah dari jawabannya; PostgreSQL juga menggunakan apa yang disebut "tulis di muka log" (secara efektif sebuah jurnal) untuk memberikan komitmen atom dan melindungi terhadap penulisan parsial, dll. Lihat postgresql.org/docs/current/static/wal-intro.html