CTE neden satır içi alt sorgulardan daha kötü?


12

Im daha iyi sorgu planlayıcısı postgresql içinde nasıl çalıştığını anlamaya çalışıyorum.

Bu sorguyu var:

select id from users 
    where id <> 2
    and gender = (select gender from users where id = 2)
    order by latest_location::geometry <-> (select latest_location from users where id = 2) ASC
    limit 50

Kullanıcı tablosunda yaklaşık 500 bin girdi ile veritabanımda 10ms'den daha az çalışır.

Sonra yinelenen alt seçimler önlemek için ben bir CTE olarak sorguyu yeniden yazabilirsiniz, şöyle düşündüm:

with me as (
    select * from users where id = 2
)
select u.id, u.popularity from users u, me 
    where u.gender = me.gender
    order by  u.latest_location::geometry <-> me.latest_location::geometry ASC
    limit 50;

Ancak, bu yeniden yazılan sorgu yaklaşık 1 saniye içinde çalışır! Bu neden oluyor? Geometri indeksini kullanmadığını açıklıyor, ama bunun için bir şey yapılabilir mi? Teşekkürler!

Sorguyu yazmanın başka bir yolu:

select u.id, u.popularity from users u, (select gender, latest_location from users where id = 2) as me 
    where u.gender = me.gender
    order by  u.latest_location::geometry <-> me.latest_location::geometry ASC
    limit 50;

Ancak, bu da CTE kadar yavaş olacaktır.

Öte yandan bana parametreleri ayıklamak ve statik olarak onları eklemek sorguyu tekrar hızlı:

select u.id, u.popularity from users u
    where u.gender = 'male'
    order by  u.latest_location::geometry <-> '0101000000A49DE61DA71C5A403D0AD7A370F54340'::geometry ASC
    limit 50;

İlk (hızlı) sorguyu açıklayın

 Limit  (cost=5.69..20.11 rows=50 width=36) (actual time=0.512..8.114 rows=50 loops=1)
   InitPlan 1 (returns $0)
     ->  Index Scan using users_pkey on users users_1  (cost=0.42..2.64 rows=1 width=32) (actual time=0.032..0.033 rows=1 loops=1)
           Index Cond: (id = 2)
   InitPlan 2 (returns $1)
     ->  Index Scan using users_pkey on users users_2  (cost=0.42..2.64 rows=1 width=4) (actual time=0.009..0.010 rows=1 loops=1)
           Index Cond: (id = 2)
   ->  Index Scan using users_latest_location_gix on users  (cost=0.41..70796.51 rows=245470 width=36) (actual time=0.509..8.100 rows=50 loops=1)
         Order By: (latest_location <-> $0)
         Filter: (gender = $1)
         Rows Removed by Filter: 20
 Total runtime: 8.211 ms
(12 rows)

İkinci (yavaş) sorguyu açıklama

Limit  (cost=62419.82..62419.95 rows=50 width=76) (actual time=1024.963..1024.970 rows=50 loops=1)
   CTE me
     ->  Index Scan using users_pkey on users  (cost=0.42..2.64 rows=1 width=221) (actual time=0.037..0.038 rows=1 loops=1)
           Index Cond: (id = 2)
   ->  Sort  (cost=62417.18..63030.86 rows=245470 width=76) (actual time=1024.959..1024.963 rows=50 loops=1)
         Sort Key: ((u.latest_location <-> me.latest_location))
         Sort Method: top-N heapsort  Memory: 28kB
         ->  Hash Join  (cost=0.03..54262.85 rows=245470 width=76) (actual time=0.122..938.131 rows=288646 loops=1)
               Hash Cond: (u.gender = me.gender)
               ->  Seq Scan on users u  (cost=0.00..49353.41 rows=490941 width=48) (actual time=0.021..465.025 rows=490994 loops=1)
               ->  Hash  (cost=0.02..0.02 rows=1 width=36) (actual time=0.054..0.054 rows=1 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 1kB
                     ->  CTE Scan on me  (cost=0.00..0.02 rows=1 width=36) (actual time=0.047..0.049 rows=1 loops=1)
 Total runtime: 1025.096 ms

3
Son zamanlarda bunu yazdım; bkz. blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences . Şu anda bu sitenin erişilebilirliğini sınırlandırabilecek bazı DNS sorunları olsa da. FROMEn iyi sonuçlar için CTE terimi yerine bir alt sorgu deneyin .
Craig Ringer

(select id, latest_location from users where id = 2)cte olarak kullanırsanız ne olur ? Belki de bu soruna neden olan şey *
cha

Karşı cinsin en yakın kullanıcılarını arayacağınızı düşünürdüm :)
cha

@ cha Cte cinsiyeti ve yerini seçmek için hız farkı yoktur. (Benim durumumda ben benzer kullanıcıların ortalamasını almak istiyorum, sadece ben soru için sorguyu basitleştirilmiş)
viblo

@CraigRinger Ben onun optimizasyon çit olduğunu sanmıyorum. Ben de öneri denedim ve aynı zamanda yavaş oldu. Öte yandan, eğer parametreleri manuel olarak çıkarırsam hızlıdır (ve benim durumumda gerçek bir seçenek, sonuç yine de bir işlevdir).
viblo

Yanıtlar:


11

Bunu dene:

with me as (
    select * from users where id = 2
)
select u.id, u.popularity from users u, me 
    where u.gender = (select gender from me)
    order by  u.latest_location::geometry <-> (select latest_location from me)::geometry ASC
    limit 50;

Hızlı plana baktığımda bana ne atlıyor (kalın):

 Sınır (maliyet = 5.69..20.11 satır = 50 genişlik = 36) (gerçek zaman = 0.512..8.114 satır = 50 döngü = 1)
   InitPlan 1 ( 0 TL döndürür )
     -> Kullanıcılar users_1 üzerinde users_pkey kullanarak Dizin Taraması (maliyet = 0.42..2.64 satır = 1 genişlik = 32) (gerçek zaman = 0.032..0.033 satır = 1 döngü = 1)
           Endeks Koşulu: (id = 2)
   InitPlan 2 ( 1 $ döndürür )
     -> Kullanıcılar üzerinde users_pkey kullanarak Dizin Taraması users_2 (maliyet = 0.42..2.64 satır = 1 genişlik = 4) (gerçek zaman = 0.009..0.010 satır = 1 döngü = 1)
           Endeks Koşulu: (id = 2)
   -> Kullanıcılar üzerinde users_latest_location_gix kullanarak Dizin Tarama (maliyet = 0.41..70796.51 satır = 245470 genişlik = 36) (gerçek zaman = 0.509..8.100 satır = 50 döngü = 1)
         Sipariş: (son_konum   $ 0 )
         Filtre: (cinsiyet = 1 ABD doları )
         Filtre ile Kaldırılan Satırlar: 20
 Toplam çalışma süresi: 8.211 ms
(12 sıra)

Yavaş sürümde, sorgu planlayıcısı eşitlik operatörünü genderve geometri operatörünü latest_locationbir birleşim bağlamında değerlendirir ; burada gelen değer meher satıra göre değişebilir (yalnızca 1 satırı doğru olarak tahmin etmiş olsa bile). Hızlı sürümde, değerler genderve skalerlatest_location olarak kabul edilir, çünkü satır planlı alt sorgular tarafından yayılırlar, bu da sorgu planlayıcısına başa çıkmak için her birinin yalnızca bir değerine sahip olduğunu söyler. Değişmez değerleri yapıştırdığınızda hızlı planı almanızın nedeni de budur.


Ben kaldırmak düşünüyorum medan fromşimdi fıkra.
Jarius Hebzo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.