Bir tstzrange
değişkende tam bir eşitlik gerektiren bir güncelleme yapıyorum . ~ 1M satır değiştirilir ve sorgu ~ 13 dakika sürer. Bunun sonucu buradaEXPLAIN ANALYZE
görülebilir ve gerçek sonuçlar, sorgu planlayıcısı tarafından tahmin edilenlerden oldukça farklıdır. Sorun şu ki, endeks taramasının açık kalması tek bir satırın dönmesini bekliyor.t_range
Bu durum, aralık tiplerine ilişkin istatistiklerin diğer tiplerden farklı şekilde depolandığı gerçeğiyle ilgili gibi görünmektedir. pg_stats
Sütun görünümüne bakıldığında n_distinct
, -1 olur ve diğer alanlar (örneğin most_common_vals
, most_common_freqs
) boştur.
Ancak, bir t_range
yerde depolanan istatistikler olması gerekir . Kesin bir eşitlik yerine t_range'de bir 'inside' kullandığım son derece benzer bir güncelleme yapmak yaklaşık 4 dakika sürüyor ve önemli ölçüde farklı bir sorgu planı kullanıyor ( buraya bakın ). İkinci sorgu planı bana mantıklı geliyor çünkü temp tablosundaki her satır ve tarih tablosunun önemli bir bölümü kullanılacak. Daha önemlisi, sorgu planlayıcısı, filtre açık için yaklaşık olarak doğru sayıda satır öngörür t_range
.
Dağılımı t_range
biraz sıradışı. Bu tabloyu başka bir tablonun tarihsel durumunu saklamak için kullanıyorum ve diğer tablonun değişiklikleri aynı anda büyük dökümlerde gerçekleşti, bu nedenle çok belirgin değerleri yok t_range
. İşte benzersiz değerlerin her birine karşılık gelen sayımlar 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
t_range
Yukarıda belirtilenler için sayımlar tamamlanmıştır, bu nedenle kardinalite ~ 3M'dir (bunun ~ 1M'si her iki güncelleme sorgusundan etkilenecektir).
Neden 1. sorgu 2. sorgudan çok daha düşük performans gösteriyor? Benim durumumda, sorgu 2 iyi bir alternatiftir, ancak kesin bir aralık eşitliği gerçekten gerekliyse, Postgres'in daha akıllı bir sorgu planı kullanmasını nasıl sağlayabilirim?
Dizinli tablo tanımı (alakasız sütunları bırakma):
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)
Sorgu 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;
Sorgu 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;
Q1, 999753 satırları güncelleştirir ve Q2, 999753 + 36791 = 1036544'ü güncelleştirir (yani geçici tablo, zaman aralığı koşuluyla eşleşen her satırın güncelleneceği şekildedir).
Bu sorguyu @ ypercube adlı kullanıcının yorumuna yanıt olarak denedim :
Sorgu 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;
Sorgu planı ve sonuçları ( buraya bakın ) önceki iki vaka arasında (~ 6 dakika) orta derecedeydi.
2016/02/05 EDIT
1,5 yıl sonra verilere artık erişemiyor, aynı yapıya (indekssiz) ve benzer kardinaliteye sahip bir test masası oluşturdum. jjanes'ın cevabı , nedenin güncelleme için kullanılan geçici tablonun sıralanması olabileceğini öne sürdü. Hipotezi doğrudan test edemedim çünkü erişemem track_io_timing
(Amazon RDS kullanarak).
Genel sonuçlar çok daha hızlıydı (birkaç faktöre göre). Sanırım bunun Erwin'in cevabıyla tutarlı olan endekslerin kaldırılması yüzünden olduğunu düşünüyorum .
Bu test durumunda, Sorgu 1 ve 2 temelde aynı miktarda zaman aldı, çünkü ikisi de birleştirme birleştirmesini kullandılar. Yani, Postgres'in karma birleştirmeyi seçmesine neden olan şeyi tetikleyemedim, bu nedenle Postgres'in neden düşük performans gösteren karma birleşimi ilk tercih ettiği konusunda netlik duymuyorum.
(lower(t_range),upper(t_range))
, eşitliği kontrol ettiğinizden beri düzenli bir btree endeksi eklemek olacaktır .
(a = b)
ikiye koşulları "içerir":(a @> b AND b @> a)
? Plan değişiyor mu?