Saya sedang melakukan pembaruan di mana saya membutuhkan kesetaraan yang tepat pada suatu tstzrange
variabel. ~ 1M baris dimodifikasi, dan kueri membutuhkan waktu ~ 13 menit. Hasil EXPLAIN ANALYZE
dapat dilihat di sini , dan hasil aktual sangat berbeda dari yang diperkirakan oleh perencana kueri. Masalahnya adalah bahwa pemindaian indeks pada t_range
mengharapkan satu baris akan dikembalikan.
Ini tampaknya terkait dengan fakta bahwa statistik pada berbagai jenis disimpan secara berbeda dari yang jenis lainnya. Melihat pg_stats
tampilan untuk kolom, n_distinct
adalah -1 dan bidang lainnya (misalnya most_common_vals
, most_common_freqs
) kosong.
Namun, harus ada statistik yang disimpan di t_range
suatu tempat. Pembaruan yang sangat mirip di mana saya menggunakan 'dalam' pada t_range alih-alih kesetaraan yang tepat membutuhkan waktu sekitar 4 menit untuk melakukan, dan menggunakan rencana kueri yang sangat berbeda (lihat di sini ). Rencana kueri kedua masuk akal bagi saya karena setiap baris di tabel temp dan sebagian besar dari tabel sejarah akan digunakan. Lebih penting lagi, perencana kueri memprediksi jumlah baris yang kira-kira benar untuk filter aktif t_range
.
Distribusi t_range
agak tidak biasa. Saya menggunakan tabel ini untuk menyimpan status historis tabel lain, dan perubahan ke tabel lainnya terjadi sekaligus dalam dump besar, jadi tidak ada banyak nilai yang berbeda t_range
. Berikut adalah jumlah yang sesuai dengan masing-masing nilai unik t_range
:
t_range | count
-------------------------------------------------------------------+---------
["2014-06-12 20:58:21.447478+00","2014-06-27 07:00:00+00") | 994676
["2014-06-12 20:58:21.447478+00","2014-08-01 01:22:14.621887+00") | 36791
["2014-06-27 07:00:00+00","2014-08-01 07:00:01+00") | 1000403
["2014-06-27 07:00:00+00",infinity) | 36791
["2014-08-01 07:00:01+00",infinity) | 999753
Hitungan untuk perbedaan di t_range
atas selesai, jadi kardinalitasnya adalah ~ 3M (yang ~ 1M akan dipengaruhi oleh salah satu permintaan pembaruan).
Mengapa kueri 1 berkinerja jauh lebih buruk daripada kueri 2? Dalam kasus saya, kueri 2 adalah pengganti yang baik, tetapi jika kesetaraan rentang yang tepat benar-benar diperlukan, bagaimana saya bisa membuat Postgres menggunakan rencana kueri yang lebih cerdas?
Definisi tabel dengan indeks (menjatuhkan kolom yang tidak relevan):
Column | Type | Modifiers
---------------------+-----------+------------------------------------------------------------------------------
history_id | integer | not null default nextval('gtfs_stop_times_history_history_id_seq'::regclass)
t_range | tstzrange | not null
trip_id | text | not null
stop_sequence | integer | not null
shape_dist_traveled | real |
Indexes:
"gtfs_stop_times_history_pkey" PRIMARY KEY, btree (history_id)
"gtfs_stop_times_history_t_range" gist (t_range)
"gtfs_stop_times_history_trip_id" btree (trip_id)
Pertanyaan 1:
UPDATE gtfs_stop_times_history sth
SET shape_dist_traveled = tt.shape_dist_traveled
FROM gtfs_stop_times_temp tt
WHERE sth.trip_id = tt.trip_id
AND sth.stop_sequence = tt.stop_sequence
AND sth.t_range = '["2014-08-01 07:00:01+00",infinity)'::tstzrange;
Pertanyaan 2:
UPDATE gtfs_stop_times_history sth
SET shape_dist_traveled = tt.shape_dist_traveled
FROM gtfs_stop_times_temp tt
WHERE sth.trip_id = tt.trip_id
AND sth.stop_sequence = tt.stop_sequence
AND '2014-08-01 07:00:01+00'::timestamptz <@ sth.t_range;
Pembaruan Q1 999753 baris dan pembaruan Q2 999753 + 36791 = 1036544 (yaitu, tabel temp sedemikian rupa sehingga setiap baris yang cocok dengan kondisi rentang waktu diperbarui).
Saya mencoba pertanyaan ini sebagai tanggapan terhadap komentar @ ypercube :
Kueri 3:
UPDATE gtfs_stop_times_history sth
SET shape_dist_traveled = tt.shape_dist_traveled
FROM gtfs_stop_times_temp tt
WHERE sth.trip_id = tt.trip_id
AND sth.stop_sequence = tt.stop_sequence
AND sth.t_range <@ '["2014-08-01 07:00:01+00",infinity)'::tstzrange
AND '["2014-08-01 07:00:01+00",infinity)'::tstzrange <@ sth.t_range;
Rencana kueri dan hasil (lihat di sini ) adalah antara antara dua kasus sebelumnya (~ 6 menit).
2016/02/05 EDIT
Tidak lagi memiliki akses ke data setelah 1,5 tahun, saya membuat tabel uji dengan struktur yang sama (tanpa indeks) dan kardinalitas serupa. jawaban jjanes mengusulkan bahwa penyebabnya mungkin adalah urutan tabel sementara yang digunakan untuk pembaruan. Saya tidak dapat menguji hipotesis secara langsung karena saya tidak memiliki akses ke track_io_timing
(menggunakan Amazon RDS).
Hasil keseluruhan jauh lebih cepat (dengan beberapa faktor). Saya menduga ini karena penghapusan indeks, konsisten dengan jawaban Erwin .
Dalam kasus uji ini, kueri 1 dan 2 pada dasarnya mengambil jumlah waktu yang sama, karena keduanya menggunakan gabungan gabung. Artinya, saya tidak dapat memicu apa pun yang menyebabkan Postgres memilih hash join, jadi saya tidak memiliki kejelasan tentang mengapa Postgres memilih hash join yang berkinerja buruk.
(lower(t_range),upper(t_range))
sejak Anda memeriksa kesetaraan.
(a = b)
dua "berisi" kondisi:(a @> b AND b @> a)
? Apakah rencananya berubah?