Dizin Taraması yerine PostgreSQL Sıralı Tarama Neden?


12

Merhaba All Benim PostgreSQL veritabanı sorgum ve herkes yardımcı olabilir merak ile ilgili bir sorun var. Bazı senaryolarda benim sorgu iki tablo birleştirme için kullanılan oluşturduğum bu dizini görmezden görünüyor datave data_area. Bu olduğunda sıralı bir tarama kullanır ve çok daha yavaş bir sorgu ile sonuçlanır.

Sıralı Tarama (~ 5 dakika)

Unique  (cost=15368261.82..15369053.96 rows=200 width=1942) (actual time=301266.832..301346.936 rows=153812 loops=1)
   CTE data
     ->  Bitmap Heap Scan on data  (cost=6086.77..610089.54 rows=321976 width=297) (actual time=26.286..197.625 rows=335130 loops=1)
           Recheck Cond: (datasetid = 1)
           Filter: ((readingdatetime >= '1920-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2013-03-11 00:00:00'::timestamp without time zone) AND (depth >= 0::double precision) AND (depth <= 99999::double precision))
           ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..6006.27 rows=324789 width=0) (actual time=25.462..25.462 rows=335130 loops=1)
                 Index Cond: (datasetid = 1)
   ->  Sort  (cost=15368261.82..15368657.89 rows=158427 width=1942) (actual time=301266.829..301287.110 rows=155194 loops=1)
         Sort Key: data.id
         Sort Method: quicksort  Memory: 81999kB
         ->  Hash Left Join  (cost=15174943.29..15354578.91 rows=158427 width=1942) (actual time=300068.588..301052.832 rows=155194 loops=1)
               Hash Cond: (data_area.area_id = area.id)
               ->  Hash Join  (cost=15174792.93..15351854.12 rows=158427 width=684) (actual time=300066.288..300971.644 rows=155194 loops=1)
                     Hash Cond: (data.id = data_area.data_id)
                     ->  CTE Scan on data  (cost=0.00..6439.52 rows=321976 width=676) (actual time=26.290..313.842 rows=335130 loops=1)
                     ->  Hash  (cost=14857017.62..14857017.62 rows=25422025 width=8) (actual time=300028.260..300028.260 rows=26709939 loops=1)
                           Buckets: 4194304  Batches: 1  Memory Usage: 1043357kB
                           ->  Seq Scan on data_area  (cost=0.00..14857017.62 rows=25422025 width=8) (actual time=182921.056..291687.996 rows=26709939 loops=1)
                                 Filter: (area_id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
               ->  Hash  (cost=108.49..108.49 rows=3349 width=1258) (actual time=2.256..2.256 rows=3349 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 584kB
                     ->  Seq Scan on area  (cost=0.00..108.49 rows=3349 width=1258) (actual time=0.007..0.666 rows=3349 loops=1)
 Total runtime: 301493.379 ms

Dizin Taraması (~ 3 saniye) ( expla.depesz.com sitesinde )

Unique  (cost=17352256.47..17353067.50 rows=200 width=1942) (actual time=3603.303..3681.619 rows=153812 loops=1)
   CTE data
     ->  Bitmap Heap Scan on data  (cost=6284.60..619979.56 rows=332340 width=297) (actual time=26.201..262.314 rows=335130 loops=1)
           Recheck Cond: (datasetid = 1)
           Filter: ((readingdatetime >= '1920-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2013-03-11 00:00:00'::timestamp without time zone) AND (depth >= 0::double precision) AND (depth <= 99999::double precision))
           ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..6201.51 rows=335354 width=0) (actual time=25.381..25.381 rows=335130 loops=1)
                 Index Cond: (datasetid = 1)
   ->  Sort  (cost=17352256.47..17352661.98 rows=162206 width=1942) (actual time=3603.302..3623.113 rows=155194 loops=1)
         Sort Key: data.id
         Sort Method: quicksort  Memory: 81999kB
         ->  Hash Left Join  (cost=1296.08..17338219.59 rows=162206 width=1942) (actual time=29.980..3375.921 rows=155194 loops=1)
               Hash Cond: (data_area.area_id = area.id)
               ->  Nested Loop  (cost=0.00..17334287.66 rows=162206 width=684) (actual time=26.903..3268.674 rows=155194 loops=1)
                     ->  CTE Scan on data  (cost=0.00..6646.80 rows=332340 width=676) (actual time=26.205..421.858 rows=335130 loops=1)
                     ->  Index Scan using data_area_pkey on data_area  (cost=0.00..52.13 rows=1 width=8) (actual time=0.006..0.008 rows=0 loops=335130)
                           Index Cond: (data_id = data.id)
                           Filter: (area_id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
               ->  Hash  (cost=1254.22..1254.22 rows=3349 width=1258) (actual time=3.057..3.057 rows=3349 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 584kB
                     ->  Index Scan using area_primary_key on area  (cost=0.00..1254.22 rows=3349 width=1258) (actual time=0.012..1.429 rows=3349 loops=1)
 Total runtime: 3706.630 ms

Tablo Yapısı

Bu tablo için tablo yapısıdır data_area. Gerekirse diğer tabloları sağlayabilir.

CREATE TABLE data_area
(
  data_id integer NOT NULL,
  area_id integer NOT NULL,
  CONSTRAINT data_area_pkey PRIMARY KEY (data_id , area_id ),
  CONSTRAINT data_area_area_id_fk FOREIGN KEY (area_id)
      REFERENCES area (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT data_area_data_id_fk FOREIGN KEY (data_id)
      REFERENCES data (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
);

SORGU

WITH data AS (
    SELECT * 
    FROM data 
    WHERE 
        datasetid IN (1) 
        AND (readingdatetime BETWEEN '1920-01-01' AND '2013-03-11') 
        AND depth BETWEEN 0 AND 99999
)
SELECT * 
FROM ( 
    SELECT DISTINCT ON (data.id) data.id, * 
    FROM 
        data, 
        data_area 
        LEFT JOIN area ON area_id = area.id 
    WHERE 
        data_id = data.id 
        AND area_id IN (28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11) 
) as s;

153812Satırları döndürür . set enable_seqscan= false;Sıralı taramayı devre dışı bırakmak ve dizin sonucunu almak için yaptım .

Ben bir ANALYSEveritabanı üzerinde çalıştım ve sorgu kullanılan sütunlarda toplanan istatistikleri artırmak, ama hiçbir şey yardımcı görünüyor.

Herkes bu yaymak ve ışık ya da denemek gerekir başka bir şey önerebilir?


Bu yürütme planlarının her birini oluşturan sorguları eklerseniz bana yardımcı olur .
Mike Sherrill 'Cat Recall'

Tahmini satır sayısında ve gerçek satır sayısında 2 büyüklük sırası farkı var mı? Bunu doğru mu okuyorum?
Mike Sherrill 'Cat Recall'

@Catcall Sorguyu ekledik (neler olup bittiğini anlamak için biraz temel). Tahmin edilen satırlara atıfta bulunduğunuzda, 200 ve daha sonra 153812 döndürüyor mu?
Mark Davidson

2
Evet, 200 vs 150k bir bakışta tuhaf görünüyor. Sol birleşimi Kartezyen bir ürünle ( FROM data, data_area) karıştırmak için zorlayıcı bir neden var mı ? İlk bakışta, ORDER BY deyimi olmadan DISTINCT ON kullanmak kötü bir fikir gibi görünüyor.
Mike Sherrill 'Cat Recall'

expla.depesz.com/s/Uzin bilgilendirici olabilir.
Craig Ringer

Yanıtlar:


8

Bu satıra dikkat edin:

->  Index Scan using data_area_pkey on data_area  (cost=0.00..52.13 rows=1 width=8) 
    (actual time=0.006..0.008 rows=0 loops=335130)

Toplam maliyeti hesaplarsanız, döngüler göz önüne alındığında, olur 52.3 * 335130 = 17527299. Bu, seq_scanalternatif için 14857017.62'den daha büyük . Bu yüzden indeksi kullanmaz.

Dolayısıyla optimizer, indeks taramasının maliyetini fazla tahmin ediyor. Verilerinizin (kümelenmiş bir dizin veya nasıl yüklendiği nedeniyle) dizinde sıralandığını ve / veya bol miktarda önbellek ve / veya güzel bir hızlı diskiniz olduğunu tahmin ediyorum. Bu nedenle, devam etmekte olan çok az rastgele G / Ç vardır.

Ayrıca kontrol etmelisiniz correlationin pg_statsendeksi maliyeti hesaplanırken kümeleme değerlendirmek ve son olarak değiştirmeyi deneyin optimizer tarafından kullanılır, random_page_costve cpu_index_tuple_costsistem eşleşecek.


Ben bir şey eksik sürece, bence @jop demek 52.13değil 52.3, bu 17470326.9 (hala seq_scan daha büyük) ile sonuçlanır
BotNet

2

CTE'niz aslında birkaç WHEREkoşulu `` dış kaynak '' dan başka bir şey yapmıyor , çoğu eşdeğer görünüyor WHERE TRUE. CTE'ler genellikle bir optimizasyon çitinin arkasında (kendi başına optimize edildiği anlamına gelir) olduğundan, belirli sorgularda çok yardımcı olabilirler. Ancak bu durumda tam tersi bir etki beklerim.

Ne denemek mümkün olduğunca basit sorgu yeniden yazmak için:

SELECT d.id, * 
FROM 
    data d 
    JOIN data_area da ON da.data_id = d.id
    LEFT JOIN area a ON da.area_id = a.id 
WHERE 
    d.datasetid IN (1) 
    AND da.area_id IN (28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11) 
    AND (readingdatetime BETWEEN '1920-01-01' AND '2013-03-11') -- this and the next condition don't do anything, I think
    AND depth BETWEEN 0 AND 99999
;

ve ardından dizinin kullanılıp kullanılmadığını kontrol edin. Yine de tüm çıktı sütunlarına ihtiyacınız olmayabilir (en azından bağlantı tablosunun iki sütunu gereksizdir).

Lütfen geri rapor verin ve hangi PostgreSQL sürümünü kullandığınızı bize bildirin.


Öneriniz için teşekkürler, yazınıza geciken yanıtım için özür dilerim, diğer projeler üzerinde çalışıyorum. Önerin gerçekten sorgu şimdi tüm sorguları için güvenilir bir şekilde dizin kullanmak gibi görünüyor ama yine de onunla beklediğiniz performansı elde edemiyorum anlamına gelir. Daha fazla veri içeren bir sorgu üzerinde analiz yaptım expla.depesz.com/s/1yu INDEX taramasında harcanan zamanın% 95'i ile 4 dakika sürüyor.
Mark Davidson

9.1.4 sürümünü kullanıyorum
Mark Davidson

Temel olarak indeks taraması oldukça hızlıdır, sorun birkaç milyon kez tekrarlanmasıdır. SET enable_nestloop=offSorguyu çalıştırmadan önce ne elde edersiniz ?
dezso

-1

Takipçiler için benzer bir sorun yaşadım

select * from table where bigint_column between x and y and mod(bigint_column, 10000) == z

Sorun benim x ve y arasındaki "bigint_column" bir dizin vardı, ama benim sorgu temelde bu tablodaki "tüm satırları", bu yüzden [zaten tüm tablo taramak zorunda beri] dizin kullanmıyordu oldu ama seq_scan sıralı taraması yapıyordu. Benim için bir düzeltme , bir ifadede bunu kullanabilmesi için denklemin "mod" tarafı için yeni bir dizin oluşturmaktı .


1
downvoters neden olduğu gibi yorum bırakmaktan çekinmeyin :)
rogerdpack
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.